Poetzsch-Heffter CBSD-Textbook 2009 PDF

You might also like

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

Software Engineering for

Embedded Systems

Textbook e-m.4

Component-Based
Software Development
Author
Prof. Dr. Arnd Poetzsch-Heffter

This work is protected by copyright. All rights thus conferred, particularly the rights to copy and
disseminate as well as the rights to translate and reprint the entire work or parts thereof, are reserved. Beyond the permissions regulated by copyright, no part of this work may be reproduced
in any way (print, photocopy, microfilm, or any other process) nor processed, copied, or disseminated using electronic systems without the written permission of the University of Kaiserslautern.

1st edition 2009

Table of Contents

Table of Contents
Glossary

About the Author

ix

References

xi

Objectives of the Textbook

xv

1
1.1
1.2
1.3
1.4
1.5
1.6

Introduction to Component-Based Software Development


Learning objectives of this chapter
Goals of CBSD
Approaching component-based software development
CBSD as a software engineering discipline
How to read this textbook
Problems for Chapter 1

1
1
2
4
10
11
12

2
2.1
2.2
2.3
2.3.1
2.3.2
2.3.3
2.3.4
2.4
2.4.1
2.4.2
2.4.3
2.4.4
2.4.5
2.5
2.6

Component Software
Learning objectives of this chapter
Software systems and component software
Software components
Component specification
Component implementation
Component composition
Deployment
Component models
Component behavior and communication
MTC: Multi-threaded components
ACC: Asynchronously communicating components
SCC: Synchronously communicating components
CSC: Clock-synchronous components
Component infrastructure and framework support
Problems for Chapter 2

15
16
16
20
22
29
31
35
37
37
39
40
43
46
48
50

3
3.1
3.2
3.3
3.3.1
3.3.2
3.3.3
3.3.4
3.3.5
3.4
3.4.1
3.4.2
3.5

Developing Component-Based Systems


Learning objectives for this chapter
An explanation of CBSD
CBSD and software engineering processes
Development of new systems
Evolution of software systems
Further development aspects
Component selection and adaptation
Quality assurance
Software architecture as skeleton for reuse
CBSD and software architecture
CBSD and other development techniques
Language and tool support

53
54
54
56
56
57
58
58
60
60
61
65
68

ii

Table of Contents

3.6

Problems for Chapter 3

68

4
4.1
4.2
4.2.1
4.2.2
4.2.3

General Component Framework Concepts


Learning objectives for this chapter
Component frameworks: Notions and core concepts
Layers: platforms, containers, and components
Components: identifiers, bodies and specifications
Composition mechanisms: bundling, deploying and connecting
components
Illustrating component framework concepts
OSGi: A component framework for Java
Basic concepts of OSGi
More about package management and bundles
More about dynamic aspects
Further aspects of OSGi
Other features of component frameworks
Problems for Chapter 4

71
71
72
72
74

4.2.4
4.3
4.3.1
4.3.3
4.3.3
4.3.4
4.4
4.5
5
5.1
5.2
5.3

75
76
83
84
91
96
98
99
101

5.4
5.4.1
5.4.2
5.4.3
5.4.4
5.5

Distribution and Heterogeneity in Component Frameworks


Learning objectives of this chapter
Distribution: problems and concepts
Heterogeneity: multi-language development for systems running on
different platforms
Common Object Request Broker Architecture
Basics of the Common ORB Architecture
Development of CORBA-based systems
CORBA infrastructure and services
CCM: The CORBA Component Model
Problems for Chapter 5

103
103
103
108
109
110
113
119
122
124

6
6.1
6.2
6.3
6.3.1
6.3.2
6.3.3
6.3.4
6.3.5
6.3.6
6.3.7
6.4
6.4.1
6.4.2
6.5

Component Frameworks for Application Serv ers


Learning objectives of this chapter
Basic concepts of application servers
Enterprise JavaBeans
Architectural overview of EJB
What beans look like
How beans are deployed
How beans are used by clients
How beans are used together
Simplifying bean development: EJB 3.0 annotations
Further aspects of EJB
Dependency injection and bean composition
Inversion of control and dependency injection
Dependency injection and Enterprise JavaBeans
Problems for Chapter 6

127
127
128
129
129
132
136
139
140
143
145
147
148
149
151

iii

Table of Contents

7
7.1
7.2
7.3
7.3.1

Components for Embedded Software


153
Learning objectives of this chapter
153
Requirements for embedded component software
153
Frameworks for embedded software
154
Pin: A domain-unspecific component framework for embedded systems
155
7.3.2 PECOS: A CBSD approach to field devices
156
7.3.3 Koala: A CBSD framework for consumer electronics
157
7.3.4 AUTOSAR: AUTomotive Open System ARchitecture
158
7.4
Problems for Chapter 7
159
8
8.1
8.2

Review and Current Developments


Review
Current developments

161
161
163

Solutions to the Problems

165

Index

175

iv

Table of Contents

Glossary

Glossary
Active component: An active component has at least one thread running in the
component that is the source of communication with the environment.
Adaptation: Component adaptation means modifying functional, non-functional,
or technical properties of a component such that the adapted component fits the
needs of the composition context.
AUTOSAR: AUTOSAR is a joint initiative of a number of major players in the
car industry to develop an AUtomotive Open System ARchitecture and to overcome the current situation characterized by proprietary solutions that hinder the
exchange of components and applications among automotive OEMs and suppliers.
Behavior: By the behavior of a component, we mean the possible communications a component can have with its environment. The behavior of a passive component can be described by the reactions to methods called on the component or
the reactions to messages sent to the component. An active component can communicate with the environment without a prior incoming call or message.
Callback: A callback scenario occurs when a component CP1 calls a method m
implemented by a component CP2 and when the execution of m leads to a call to
a method of CP1.
Common Object Request Broker Architecture: See CORBA
Component: See software component and program component.
Component-based software development: Component-based software development is a sub-discipline of software engineering. Its goal is to provide concepts,
methods, languages, techniques, frameworks, tools, standards, and organizational
infrastructure for the development, provision, and selection of reusable software
components as well as for the development of component-based software systems
(in the sense below) using these components.
Component-based software system: A component-based software system is a
software system whose implementation is composed of software components according to the definition given below. Furthermore, we require that the component
structure refines the (described) architecture of the system.
Component container: The container of a component framework provides the
functionality for managing components. The container is executed in its deployed,
i.e., a component C is deployed in a container D. The container handles registration and connection of components. Furthermore, it makes general services available. The container is part of the component infrastructure and independent of the
developed component-based systems.

vi

Glossary

Component framework: A component framework consists of a component


model together with a coherent component infrastructure. The infrastructure typically includes containers in which components are deployed and services that all
components can use. Component frameworks support developers in building
component software. In particular, they provide means that go beyond classical
programming languages and programming environments.
Component model: Component models determine the common computation,
communication, and composition principles and rules underlying a class of components. In addition, a component model can describe other common aspects of its
components, for example the available reflexivity mechanisms supported by the
components. Components adhering to different component models are not ready
for composition.
Composition kind: We distinguish three kinds of composition: Hierarchical
composition allows building new components by composing given components
such that the new component and its constituents follow the same component
model. If the new component follows a different model than the constituents, we
speak of heterogeneous composition. If the composition does not result in a new
component, but in a web of components, we speak of open composition.
Container: See component container.
CORBA: The Common Object Request Broker Architecture (CORBA) is a vendor-neutral standard for distributed object-oriented computing defined by the Object Management Group (OMG). CORBA enables software systems written in
multiple computer languages and running on multiple computers to work together.
Dependency injection: Dependency injection means to establish the connection
of a component C to its (initially) required services automatically from the outside
of C. In a component framework, it is usually the container that injects the service
references. Required service references can be passed as arguments to the constructor (constructor injection), can be injected by explicit setter methods (setter
injection), or can be provided by additional methods in the component interface.
Deployment: In general, deployment includes the bundling of the program code
for components and possibly glue code into deployable units and making these
units available on the target platforms. In component frameworks with explicit
containers, deployment means making the name and body of components C available within a container D. Names and code used by C are resolved with respect to
the current name space of D. Deployment happens before any use of C. Deployment can happen statically or dynamically.
Deployment descriptor: Deployment descriptors describe properties of deployable components in a formal syntax. Simple examples are the manifest files of
OSGi and .Net describing the contents and dependencies of the deployable OSGi
bundles and .Net assemblies, respectively. More comprehensive are, for example,

Glossary

the descriptors of EJB defining further properties like access rights, transactional
behavior, and persistence management.
Development stages: Software components and systems can be in different stages
of development. They can be designed, programmed, configured, deployed/ installed, and executing.
Embedded system: An embedded system is a computer system designed to perform one or several dedicated functions, often with real-time computing constraints. It is embedded into a complete device, often including computer hardware and mechanical parts.
Enterprise JavaBeans (EJB): The Enterprise JavaBeans framework supports the
development of component-based, distributed, transactional business applications
and their deployment within application servers. Components within the framework are called Enterprise JavaBeans or simply EJBs or beans. The EJB framework specification was created under the Java Community Process to provide
public participation in its definition and development.
Glue code: Glue code is code written in a scripting or programming language that
performs the composition of component instances either locally in one process or
remotely by using communication mechanisms.
Environment: Software components and systems never operate in isolation. They
interact with human users or other technical systems either via software or by directly using sensors and actors. The interaction includes user inputs, outputs for
users, messages, sensor values or calls from system parts in the environment, as
well as messages, output, or calls to such parts. We speak of the environment of a
software component or system to refer to the outside world it interacts with.
Implementation: Implementation mainly consists of the development of the programs realizing system components. It can include building aspects and preparation of deployment.
Interface: The term interface is overloaded. The interface of an object or port describes the methods or messages that can be used to communicate with the object
or port. The interface of a component comprises the interfaces it provides at its
ports and the interfaces it requires from other components.
Interface definition language (IDL): Interface definition languages allow defining the methods and types of component interfaces in a way that is independent of
the programming language. An example of such a language is the CORBA IDL.
OEM: OEM is the abbreviation for original equipment manufacturer.
OSGi: OSGi is a component framework for Java. OSGi originally stood for Open
Service Gateway initiative. Currently, it is marketed under the header OSGi
The dynamic module system for Java (see http://www.osgi.org). The OSGi
framework is supported by a large non-profit industry alliance.

vii

viii

Glossary

Passive component: A passive component only reacts to method calls and incoming messages. It has no thread of its own that initiates communication.
Persistence: Persistence means storing and maintaining data between different
runs of one or several systems. In many domains and areas, component-based applications have to manage persistent data. Component frameworks support this
important task by connecting the data state of components to relational databases.
Platform: The platform for a software system or component is the software/hardware system on which it is deployed. The platform provides the basic
functionality and resources. For component systems, it typically includes the
computer hardware, the operating system, middleware, and potentially a component container and framework.
Port: The access points for communicating with components are called ports.
Program component: A program component is a part of a program written in
some programming language, such as a procedure or a program module. Program
components can be used as implementations for software components, but not
every program component satisfies the properties we require for software components.
Remote method invocation (RMI): Remote method invocation (RMI) allows
calling methods on references of objects outside the local process. RMI is more
general than a remote procedure call, because the caller does not have to know
where the receiver object is.
Software component: A software component is a part of a software system or an
entity used for composing software systems. More precisely: Software components have well-defined interfaces and a behavior that can be described independent of the contexts in which they are used. Software components have an implementation that can be independently composed and deployed. To be composable,
software components follow a component model that determines the common
computation, communication, and composition principles and rules underlying a
class of components.
Stages: see development stages

About the Author

About the Author


Poetzsch-Heffter, Arnd
Received his PhD and Habilitation degrees in Informatics from Technische Universitt Mnchen in 1991 and 1997, respectively. From 1996 to 2001, he was an
associate professor at FernUniversitt Hagen. Since 2002, he has been a full professor at the University of Kaiserslautern, leading the software technology group.
The main research interests of the group are in object-oriented programming, language-based software tools, component technology and automated composition,
as well as in specification and verification techniques for programs and components. Poetzsch-Heffter is the author of a textbook on object-oriented programming and a co-founder and steering committee member of the international workshop series Formal Techniques for Java-like Programs. He serves as an expert
for the German National Science Foundation and the Dutch National Science
Foundation. His professional services include several positions in the German Informatics Association (GI) and participation in a number of program committees
as a member and chair. He is a member of the IFIP working group 2.4 Software
Implementation Technology.

ix

About the Author

References

References
[1]

Jantsch, A.: Modeling embedded systems and SOC's. Morgan Kaufmann


Publishers, 2004.

[2]

Rausch, A.; Reussner R.; Mirandola R.; Plasil, F. (Eds.): The common component modeling example: Comparing software component models. LNCS
5153, Springer-Verlag, 2008.

[3]

Szyperski, C. with Gruntz, D; Murer, S.: Component software: Beyond object-oriented programming. Second Edition, Addison-Wesley, 2002.

[4]

Wang, A. J. A.; Qian, K.: Component-oriented programming. Wiley, 2005.

[5]

Alfaro, L. de; Henzinger, T.: Interface automata. In: Proc. of the


ESEC/FSE-9, pp. 109-120. ACM Press, 2001.

[6]

Amann, U.: Invasive software composition. Springer-Verlag, 2003.

[7]

Armstrong, J.; Virding, R.; Wikstrm, C.; Williams, M.: Concurrent programming in Erlang. Second Edition. Prentice Hall, 1996.

[8]

Armstrong, R.; Kumfert, G.; McInnes, L. C.; Parker, S.,; Allan, B.; Sottile,
M.; Epperly, T.; Dahlgren, T.: The CCA component model for highperformance scientific computing. Concurrency and Computation: Practice
and Experience, 18(2):215-229, 2006.

[9]

Bass, L.; Clements, P.; Kazman, R.: Software architecture in practice. Addison-Wesley, 1998.

[10] Becker, S.; Koziolek, H.; Reussner, R.: The Palladio component model for
model-driven performance prediction. Journal of Systems and Software,
82(1):3-22, 2009.
[11] Benton, N.; Cardelli, L.; Fournet, C.: Modern concurrency abstractions for
C#. In B. Magnusson (ed.): Proc. ECOOP 2002, LNCS 2374, pp. 415-440.
Springer-Verlag, 2002.
[12] Berry, G.: The foundations of Esterel. In: Proof, language, and interaction:
essays in honour of Robin Milner, pp. 425454. MIT Press, 2000.
[13] Binnema, D.-J.: Bonobo components: Architecture and application (available online).
[14] Bolton, F.: Pure CORBA. Sams, 2001.
[15] Bruneton, E.; Coupaye, T.; Leclercq, M.; Quma, V.; Stefani, J.-B.: The
Fractal component model and its support in Java. Software practice and ex-

xi

xii

References

perience (special issue on experiences with auto-adaptive and reconfigurable systems), 36(11-12):1257 - 1284, 2006.
[16] Buschmann, F.; Meunier, R.; Rohnert, H.; Sommerlad, P.; Stal, M.: Patternoriented software architecture: A system of patterns. John Wiley, 1996.
[17] D'Souza, D. F.; Wills, A. C.: Objects, components, and frameworks with
UML. The Catalysis Approach. Addison-Wesley, 1999.
[18] Gao, J.; Tsao, H.-S. J.; Wu, Y.: Testing and quality assurance for component-based software. Artech House, 2003.
[19] Goetz, B. with Peilerls, T; Bloch, J.; Bowbeer, J.; Holmes, D; Lea, D.: Java
concurrency in practice. Addison-Wesley, 2006.
[20] Griffel, F.: Componentware: Konzepte und Techniken eines Softwareparadigmas. dpunkt.verlag, 1998.
[21] Heinecke, H.; Damm, W.; Josko, B.; Metzner, A.; Kopetz, H.; Singionvanni-Vincentelli, A.; Di Natale, M.: Software components for reliable automotive systems. In: Proc. of the conference on design, automation, and test in
Europe. ACM, 2008.
[22] Heineman, G. T.: Councill, W. T.: Component-based software engineering:
Putting the pieces together. Addison-Wesley, 2001.
[23] Hissam, S.; Ivers, J.; Plakosh, D.; Wallnau, K. C.: Pin component technology (V1.0) and its C interface. Technical Note CMU/SEI-2005-TN-001,
2005 (available online).
[24] Hoare, C. A. R.: Communicating Sequential Processes. Prentice Hall International, 1985. (freely available online)
[25] Hofmeister, C.; Nord, R.; Soni, D.: Applied software architecture. AddisonWesley, 2000.
[26] Johnson, R.: Expert one-on-one: J2EE design and development. Wrox
Press, 2002.
[27] Kiczales, G.; Lamping, J.; Mendhekar, A.; Maeda, C.; Lopes, C.; Loingtier,
J.-M.; Irwin, J: Aspect-oriented programming. In: Proc. of the European
Conference on Object-Oriented Programming, LNCS 1241, pp. 220242.
Springer-Verlag, 1997.
[28] Kruchten, P: The 4+1 view model of architecture. IEEE Software, 12(6):4250.
[29] McHale, C. J.: Corba explained simply. 2007 (self-published, available
online).

References

[30] Milner, R.: Communication and concurrency. Prentice Hall International,


1989.
[31] Milner, R.: Communicating and mobile systems: the -calculus. Cambridge
University Press, 1999.
[32] Muskens, J.; Chaudron, M. R. V.; Lukkien, J. J.: A Component framework
for consumer electronics. In: Component-Based Software Development for
Embedded Systems, LNCS 3778, pp. 164-184. Springer-Verlag, 2005.
[33] Necula, G. C.: Proof-carrying code. In: Proc. of the 24th ACM SIGPLANSIGACT Symposium on Principles of Programming Languages. pp. 106
119. ACM, 1997.
[34] Nierstrasz, O.; Arevalo, G.; Ducasse, S.; Wuyts, R.; Black, A. P.; Mller, P.
O.; Zeidler, C.: Genssler, T.; van den Born, R.: A component model for field
devices. In J. Bishop (Ed.): CD 2002, LNCS 2370, pp. 200209. SpringerVerlag, 2002.
[35] Ommering, R. van; Linden, F. van der; Karmer, J.; Magee, J.: The Koala
component model for consumer electronics software. IEEE Computer,
33(3): 78-85. 2000.
[36] Rausch, A.; Broy, M.; Bergner, K.; Hhn, R.; Hppner, S.: Das V-Modell
XT. Grundlagen, Methodik und Anwendungen. Springer-Verlag, 2007.
[37] Schfer, J.; Poetzsch-Heffter, A.: CoBoxes: Unifying active objects and
structured heaps. In: 10th IFIP International Conference on Formal Methods for Open Object-Based Distributed Systems (FMOODS 2008), LNCS
5051, pp. 201-219. Springer-Verlag, 2008.
[38] Shaw, M.; Garlan, D.: Software architecture: Perspective on an emerging
discipline. Prentice Hall, 1996.
[39] Trapp, M.: Model-based component engineering. Course text. Master program "Software Engineering for Embedded Systems".
[40] Ullman, J.: Database and knowledge-base systems, volume 1: classical database systems. Computer Science Press, 1988.
[41] Walls, C.; Breidenbach, R.: Spring in action. Manning Publications, 2007.
[42] Zaremski, A. M.; Wing, J. M.: Specification matching of software components. ACM Transactions on Software Engineering and Methodology,
6(4):333-369. 1997.

xiii

xiv

References

Objectives of the Textbook

Objectives of the Textbook


By studying this course on component-based software development (CBSD), you
should learn
the motivations and notions of component-based software development,
the role and benefits of CBSD in the area of software engineering,
the specific software engineering aspects in CBSD,
to build software systems from components,
the goals, structure, features, and services of component frameworks,
to apply existing component frameworks like OSGi, CORBA, and EJB.
Furthermore, you should be able to
compare component models and to classify their differences,
relate CBSD to programming paradigms and techniques,
envision future developments in the area.

xv

xvi

Objectives of the Textbook

Chapter 1 Introduction to Component-Based Software Development

Introduction to Component-Based Software


Development

Component-based software development, or CBSD for short, is a sub-discipline


of software engineering that emphasizes the composition of software systems
from well-understood logical and functional parts. CBSD is especially about concepts and technological support for components and composition.
Component-based development is applicable in all domains of software engineering. In particular, it can be used for the construction of information systems and
embedded systems. However, component technology is currently more mature
and more widely used for information system development than for embedded
systems, mainly because it is harder to satisfy the often tighter resource constraints in embedded systems.
In this course, we will describe the concepts of CBSD (Chapter 2) and explain the
relationship of CBSD to software engineering (Chapter 3). We will introduce
component frameworks, in particular OSGi (Chapter 4), and technology for heterogeneous systems, such as CORBA (Chapter 5). OSGi and CORBA are used for
both information and embedded systems. Then, we will spend one chapter on
component technology for application server development, as this is the most successful application area of CBSD with mature domain-specific support by component frameworks (like EJB and Spring). This allows us to explain important general CBSD concepts such as different component kinds, architectural framework
support, and dependency injection as part of a realistic setting. Currently, widely
used, domain-specific component models and frameworks do not exist in the area
of embedded systems. In Chapter 7, we will explain the special requirements for
embedded component software and describe some component models and frameworks discussed in the literature.
After describing the learning objectives (1.1), this chapter presents the main motivations and goals for CBSD (1.2), provides an overview of and rationale for the
topics of this textbook (1.3), discusses relationships to other software engineering
areas (1.4), and contains some advice for how to study the presented material
(1.5).

1.1

Learning objectives of this chapter

After reading this chapter, you should


know the central motivations for CBSD,
understand the basic aspects of component-based software development,

Chapter 1 Introduction to Component-Based Software Development

have obtained a good idea of what a software component is and what it means
to build software systems from components,
know the most important relationships between software engineering in general and CBSD,
have a clue as to how to work with this textbook.

1.2

Goals of CBSD

To understand the concepts, methods, and technologies of CBSD, it is important


to comprehend its motivations and goals. To explain the goals, it is sufficient to
have the following basic understanding of what a software component is:
A software component is a part of a software system or an entity for composing
software systems. More precisely:
Software components have well-defined interfaces and a behavior that can
be described independent of the contexts in which they are used.
Software components have an implementation that can be independently
composed and deployed.
To be composable, software components follow a component model that determines the common computation, communication, and composition principles underlying a class of components.
That is, as a first approximation, we essentially derive the notion of components
from that of software systems. Components may be small, e.g., an implementation
of a priority queue, or large, e.g., a file system. Composition can be done in different ways, e.g., by linking code modules together or by establishing network
connections between components running in different processes.
The main motivations and goals of component-based software development are:
Improve the quality of software systems and implementations
Improve flexibility and evolvability
Support separate development
Allow for role separation
Improve reuse
Shorten time to market
Provide the basis for a market of software components
Obviously, the motivations and goals come from different stakeholders within
software development. Whereas the first goals are related to technical aspects of

Chapter 1 Introduction to Component-Based Software Development

software development, the later goals are mainly important for economical and
management reasons. In the following, we will explain the goals and then shortly
discuss their relationships.
Quality. Component-based software development extends and refines the development principle of low coupling and high cohesion: Structure the system in
such a way that it consists of loosely coupled components that encapsulate highly
interdependent aspects. Well-defined interfaces and independent component behavior help to improve the quality of system designs and implementations.
Flexibility and evolvability. Software systems have to be adapted to new requirements. Component-based development allows maintaining and evolving the
system by exchanging components incrementally. The well-defined interfaces and
independent component behavior make clear which parts of a system are possibly
affected by changes. As we will see later in this course, component technology
allows adding or exchanging components without recompilation of the system and
support modification at runtime. These features increase flexibility during maintenance and evolution.
Separate development. Building systems from components with well-defined
interfaces and independently described behavior allows distributing the development tasks to different groups or even different companies. Provided that the overall design was correct and that the development groups respect the interfaces
and implement the required behavior, system composition and integration should
cause no problems. In case composition fails, component-based design helps to
assign blame to the groups that caused the errors. Separate development is further
simplified if the component-based design is built on standards, is able use common services, and is supported by a technology for composition.
Role separation. Role separation means that the process of developing, deploying, administering, and using a software system can be clearly separated into different roles. Component frameworks make it possible to separate the responsibility of developing and managing a software system into five roles: component provider, framework provider, application provider (constructs an application from a
number of components), application deployer (installs an application on a platform), and system administrator. Role separation is important for division of labor, specialization, improved tooling, and better competition in the software market.
Reuse. Reuse on the level of plain program code is certainly helpful for the development of a new system. However, the program code and its effects have to be
understood in detail and reuse can only be done if the reused code and the new
system are developed in the same programming language. Reusing software components is a compelling idea that overcomes restrictions. Based on the interfaces,
component frameworks enable deployment and composition of components implemented in different languages. Furthermore, having standardized component
descriptions simplifies the search for components providing the needed features.

Chapter 1 Introduction to Component-Based Software Development

Time to market. If CBSD can achieve the above goals, the development time for
a system can be reduced considerably. This can provide a competitive edge to a
software producer. And it would allow society as a whole to better exploit the potential of good software products.
Market of software components. Whereas competition on the software market is
currently mainly about complete products, a more fine-grained market of components would allow more small and mid-size companies to participate in the market. The expected results are better and cheaper software components, less dependence on quasi-monopolists, and faster technological development in the software industry and related industries as a whole.
Discussion. It should be clear that these motivations are not independent. For example, if reuse allows incorporating existing components into new products, time
to market can be reduced. What is less clear is that there is a mutual dependency
between the more technically and the more economically oriented goals. On the
one hand, component technologies are needed to enable relevant component reuse
and establish a component market. On the other hand, component technologies
will only be successful if the additional costs and care it takes to develop reusable
components is justified by the gains of CBSD.

1.3

Approaching component-based software development

To further approach component-based software development, we illustrate central


concepts and aspects of CBSD along with a small example. The basic idea of this
section is to develop some intuition as a background for the discussions and definitions during the remainder of this course. In particular, we will consider the notion of software components in relation to the basic phases of software development.
Retail store example. As an example, we will look at the development of software systems running in most retail stores nowadays (the example is a simplified
version of the system investigated in [2]). The retail store system is responsible
for managing the items in the store, placing orders to wholesalers, and registering
sold items and the payments at the cash desks. We assume that there may be multiple cash desks in a store and that every cash desk is equipped with a cash box, a
bar code scanner, and a printer. Figure 1.1 provides a schematic view on the logical components of the system. Here the IO devices are represented by the software
that allows to control them (ScannerController, etc.). To keep things simple, we
left out the connections between the components.

Chapter 1 Introduction to Component-Based Software Development

Figure 1.1:
Logical
components of the
retail store system

Analysis. To describe and analyze the behavioral requirements of a complex system, it is helpful to structure it into parts. As the behavior is usually related to the
logical view of a system (see Subsection 3.4.1), we call these parts the logical
components. The logical components are defined in such a way that it becomes as
easy as possible to state the system behavior. In our example, we used a hierarchical structuring (see Figure 1.1): The retail store system consists of a store management component and one or more cash desks. The store management consists
of a database and components for ordering new items, for booking the payments
and sold items, and for providing user access to the database. Each cash desk consists of three controllers for the input/output devices and a management component.
Based on the logical components, we can formulate the behavioral requirements.
For the example, this would especially mean to define
the possible kinds of behavior of the cash desk management: How are the interactions with the controllers? Should it be possible to take an already
scanned item out of a payment transaction? When is the booking done? What
happens if the printer fails?
the user interactions supported by the user access component: Does the user
trigger the ordering? What kinds of statistics can be produced by the system?
What should become clear here is that the logical components are primarily a
means to provide a first structure of the system that allows stating and analyzing
the behavioral requirements. During design and implementation, the logical components and their structure can be fully or largely realized by corresponding software components. This has the advantage that the description of the logical components can be reused in describing the software components. However, as we

Chapter 1 Introduction to Component-Based Software Development

will see, during design other constraints and aspects have to be taken into account.
And this might lead to a different breakdown into software components, which
does not match the structure of the logical components.
Design. The essential outcome of the design phase is the architecture of the
software system. The architecture describes the software components used to implement the system, their distribution to platforms, and their composition. The
challenge for design is that architectures have to meet constraints from quite different areas and a decision in one area often affects options in other areas. In particular, the following areas and decisions are pertinent:
1. Design of the structure of the software components realizing the functional
behavior of the system together with the communication techniques used.
2. Specification of the software components, that is, specification of their interfaces and behavior (possibly including their life cycle at runtime).
3. Specification of the schemata and formats of the data that is interchanged between components.
4. Selection of available software components (e.g., domain-specific components, database management systems, infrastructure components).
5. Configuration and adaptation of selected software components.
6. Selection of the implementation languages, frameworks, and development and
maintenance tools.
7. Selection of the physical and virtual platforms (e.g., computers and their operating systems, devices and their controllers) and their connections.
It is important to understand the often tight relationships between these design decisions. Here are two examples of such relationships in the context of the design
of the retail store system. The decision to get a bar code scanner from a certain
vendor V might affect the decision about how the cash desk management component is implemented, e.g., because the scanner controller of vendor V can only be
used together with a certain operating system and implementation language. A
decision about the distribution of the software components to the underlying computer platforms might affect the selection of the needed hardware. For example, if
we deploy the cash desk management components for all cash desks on a central
server, we need less computation power at each cash desk, which may result in
less hardware and maintenance costs.
To illustrate some possible design decisions, we shortly discuss a high-level architecture of the retail store system (see Figure 1.2). Unlike in the logical view,
where boxes represent logical components, the boxes in Figure 1.2 represent
software components, that is, we assume not only that they have well-defined
interfaces and described behavior, but that the system implementation realizes

Chapter 1 Introduction to Component-Based Software Development

these interfaces and the component behavior. By saying that the implementation realizes the components, we mean that
each component of the architecture corresponds to a module or part of the
program code,
the program code provides the interfaces of the component and implements
the behavior,
the program code is deployable independently.
Furthermore, the architecture as sketched by Figure 1.2 indicates the decision regarding the physical distribution of the system. There is one StoreServer (gray)
and one CashDeskPlatform (yellow) for each cash desk.

Figure 1.2:
Architectural
components of the
retail store system

The structure of the architecture is slightly different from the logical view, reflecting design decisions already made before. We decided in favor of a specific cash
desk computer with integrated printer and cash box. The cash desk platform
(computer and operating system) comes with a simple CashDeskControl, which is
not sufficient for our purposes. The operating system allows installing additional
programs, but programming for this platform is expensive. Thus, we decided to
place the CashDeskManagement on the StoreServer. For cost reasons, the scanner
of another vendor should be used. That is why we have to develop a ScannerAdapter, which allows to integrate the scanner with the CashDeskControl. On
the server side, we decided to use a component framework for application servers
with integrated database management system. In addition to the database schema,
we have to develop the ordering and booking component and the services and user
interfaces for user access. All software components that need development are
depicted with a bold line in Figure 1.2.

Chapter 1 Introduction to Component-Based Software Development

The details of the sketched architecture are not important for this introduction.
However, it is important to understand the differences between the logical view of
Figure 1.1 and the architecture in Figure 1.2. Whereas in the former diagram, the
components and their structure simply meant some abstractions used to explain
the overall system behavior, the components and their structure in the latter diagram reflect the implementation of the system. That is, each component in the architecture corresponds to a part of the program code realizing this component (in
the above sense). Putting it the other way round, a component of the architecture
is an abstraction of some part of the program code. This correspondence is a characterizing feature of software components.
Without going into details here, the simple example illustrates some typical aspects of CBSD:
There is an interplay between platform, languages, and tooling on the one side
and definition of components and their structure on the other side (for example, the selection of the CashDeskPlatform influenced the implementation of
the cash desk management).
Components from different vendors usually have to be adapted (e.g., a scanner
adapter was needed). Adaptation can be caused by interfaces and behavior that
do not match, but also by different implementation techniques.
The composition of components can take very different forms (in the example,
we could have the following forms: the ScannerAdapter is statically linked to
the CashDeskControl; the CashDeskControl communicates with the CashDeskManagement via sockets; the connections between ordering, booking,
and database are provided by the component framework used).
In summary, software components are independently describable entities of their
own that link the architecture to the implementation. Considered in a bottom-up
process, we can use existing software components, adapt some of them, and compose a system out of them. Considered in a top-down process, we can develop an
architecture from the requirements of the system, define the interfaces and behavior of the components, implement them separately, and finally compose them.
CBSD does not favor bottom up over top down or the other way round. The crucial thing is an architectural design based on well-defined components that can be
tracked down to the implementation.
Implementation. Implementation essentially means developing the programs that
realize components. It may include building and deployment aspects. Software
components are mediators between design and implementation. On the one hand,
they are more abstract than programs, because we require them to have an implementation-independent specification of interfaces and behavior. In particular, a
component specification can be implemented in different ways and in different
languages. On the other hand, we require software components to meet implementation-related requirements:

Chapter 1 Introduction to Component-Based Software Development

1. The program parts implementing a software component have to satisfy the


component specification. In particular, if we talk about an implemented software component, it is clear which program parts constitute the implementation.
2. A software component should be a unit of deployment.
3. Building the deployment unit from the program parts (e.g., by compilation and
linking) should be possible independent of other components.
Component implementations might have to cope with further constraints. For example, to be usable in a given component framework, they have to be developed
according to the standards, models, and rules of the framework.
Administration and maintenance. After their deployment, components can be
used. Usage means that the system consisting of the components is started or that
components are integrated into a running system by dynamically loading and linking them into it. Software component models and component frameworks simplify and often provide support for dynamic loading and linking. In addition, they
help in administering and maintaining a system, for example by allowing to replace components with new versions within a running system. Without going into
the details, it should be clear that software components can be exploited during
the startup and shutdown of parts of a system as well as in the partial modifications and stepwise evolution of a system.
Summary. Software components are units of abstraction that relate design, implementation, deployment, and maintenance activities. The views on software
components are different in the different phases:
Design considers them mainly from the outside, that is, as black boxes with
well-defined interfaces and independently described behavior.
Implementation considers them as units of programming and deployment with
a well-defined specification.
Administration and maintenance consider them as units of integration, instantiation, startup, update, shutdown, and disintegration. In particular, they are a
unit for versioning.
Although the views are different, a given software component is considered to be
the same in all phases. That is, software components are units of abstraction above
the level of programming that span different phases. Furthermore, software component models and component frameworks allow providing better tool support for
the separate development, role separation, deployment, and management of component-based systems.

10

Chapter 1 Introduction to Component-Based Software Development

1.4

CBSD as a software engineering discipline

As explained in the last section, we consider CBSD as a discipline to support the


design, implementation, deployment, and maintenance of software systems. To
some extent, component models can also help during system analysis. This section shortly sketches the relationships of CBSD to other topics in software engineering and describes where they are covered in the textbook.
Software modeling. As software systems can be very large, modeling techniques
for software systems need structuring mechanisms. Different views of software
systems need different models and structuring mechanisms. Structuring according
to CBSD means decomposing a system into communicating components with local states. In this respect, CBSD is closely related to software architecture. The
software engineering challenge in this area is to develop powerful component
models and composition mechanisms for CBSD and to show how these component models can be integrated with other models. Different component models
and composition mechanisms are a central topic of Chapter 2.
Software specification. Describing properties of software and of software interfaces is important for separate development, in particular for development in large
teams and at the boundary between different stakeholders. In the context of
CBSD, we need techniques for specifying the interfaces and behavior of components. Such specifications are the contracts between the component users and the
component providers. In particular, they provide the requirements for the component implementors. Specifications should precisely and completely describe the
behavior of components and hide implementation details. For simple component
models, specification techniques and languages exist. For realistic models, progress has been made in the last decades, but research and development have not
yet reached fully satisfying results. We will discuss specification aspects in Chapter 2.
Implementation support. Component implementation is related to techniques,
languages, and tools for programming, building, configuring, deploying, and
composing components. Having a clear notion of what a component is, how it is
named and specified can simplify all implementation steps. For specific component models, we can define and construct further infrastructure, in particular to
support composition, deployment, communication in heterogeneous distributed
systems, load balancing, runtime modification of systems, and maintenance.
Component models together with such a specific infrastructure are often called
component frameworks. Such frameworks can also support the generation of
program code from a high-level or domain-specific component description and
can help with the adaptation of components. General aspects of component models, composition techniques, component infrastructure, and frameworks are the
topic of Chapter 2. Adaptation will be described in Chapter 3. Specific component
frameworks and their characteristics will be treated in Chapters 4 to 7.

Chapter 1 Introduction to Component-Based Software Development

Quality assurance. Having a notion of software components spanning every


phase from design to runtime is crucial for quality assurance, as it allows combining the analysis techniques for different parts and phases of the software life cycle. In particular, the component specifications can be used, on the one hand, to
analyze the architecture and, on the other hand, to test and verify component implementations. Furthermore, the architectural structure allows for static and dynamic fault analysis based on the fault information of the components. Aspects of
quality assurance will be briefly addressed in Chapter 3.
Development processes. As already mentioned in Section 1.3, we consider
CBSD to be independent of specific software development processes. In particular, we do not favor bottom-up or top-down development. This is partially in contrast to other authors who combine our product-oriented conception with a specific development process:
Some authors stress that CBSD is reusable component development and assembly of software systems from software components.
Some authors stress that architecture-driven development is part of CBSD.
To underline our more focused conception, we have deliberately chosen the notion component-based software development over the notion component-based
software engineering that is used as well. The relevant aspects of development
processes for CBSD will be discussed in Chapter 3.
Qualification. Qualification is the process of selecting components and determining their suitability for use within the system under development. Qualification is
a crucial aspect of CSBD in general and of the creation of component markets in
particular. If it is not possible to find existing components and to check easily
whether they meet the given constraints directly or whether they are adaptable
with reasonable effort, reuse or multiple use are difficult to achieve and a component market cannot work. Qualification is based on the specification of and
knowledge about components and uses retrieval techniques to locate components.
Some aspects of qualification will be considered in Chapter 3.
Further aspects. The software engineering aspects introduced in this section are
meant as a background for the explanation of the more fundamental issues of
software components and CBSD in Chapter 2. After that, in Chapter 3, the software engineering aspects will be treated in more detail. In particular, we will relate CBSD to other development and structuring methods, such as serviceoriented development, product-line engineering, and aspect-oriented development.

1.5

How to read this textbook

CBSD is not a mathematical theory that can be presented based on strict definitions. Similar to a paradigm, it is a more or less consistent (semantic) web of notions, concepts, techniques, language aspects, methods, and artifacts. Such a web

11

12

Chapter 1 Introduction to Component-Based Software Development

is difficult to convey using linear text. Although we have tried hard to linearize
the information in such a way that each chapter only builds on its predecessors,
we recommend rereading Chapters 2 and 3 after having studied some of the later
chapters. The reason is that a number of issues treated in the earlier chapters are
motivated by the needs of the later chapters about component frameworks. Furthermore, we recommend consciously using the chapter and section structure of
this book as a skeleton for the explained concepts.
The rationale of the structure of this textbook is as follows. This introduction sets
the scene by explaining the goals of CBSD, building up some intuition of what
software components are, and relating CBSD to other software engineering aspects. Chapter 2 presents the fundamental notions of software systems, components, composition, and frameworks. Based on these notions, Chapter 3 explains
the development of component-based software systems and describes the relationship of CBSD to adjacent development techniques and other relevant engineering
aspects. Chapter 4 is about basic component frameworks and their features (using
OSGi as an example). Chapter 5 presents component frameworks for heterogeneous and distributed systems (using CORBA as an example). Chapter 6 deals with
component frameworks for the construction of application servers (using Enterprise Java Beans, EJB for short, as an example). Chapter 7 gives a short overview
of component technology for embedded systems. Finally, Chapter 8 concludes.
At the end of each chapter, you will find a number of self-review questions. You
should be able to answer all of them. If you are not sure about the answer, you
should go back to the corresponding text passage (the appendix provides help for
answering the questions).
Prerequisites. We assume that you, the reader, have experiences with the design
and implementation of software. You should know the basics about finite automata and should be able to write Java programs and read programs in similar languages.

1.6

Problems for Chapter 1

Problem 1.1:
Motivations for
CBSD

Problem 1.1:

Problem 1.2:
Logical and
software
components

Problem 1.2:

What are the main motivations for CBSD? Name the corresponding stakeholders
for each motivation!

What is the difference between the logical components and the software components of a system? What are reasons why the two component structures may deviate?

13

Chapter 1 Introduction to Component-Based Software Development

Problem 1.3:
What is the difference between a software component and a program component
according to the explanations of this chapter? What do we expect from a software
component that is missing if we only have a program module?

Problem 1.4:
Name the three different views of software components mentioned in the text!

Problem 1.3:
Software and
program
component

Problem 1.4:
Three views of
software
components

14

Chapter 1 Introduction to Component-Based Software Development

Chapter 2 Component Software

Component Software

Component software is built from software components. To be composable, software components have to adhere to a component model describing what components look like and how they are composed. In general, components following different component models cannot be composed. Component models can differ in
many aspects:
What are component interfaces and how are they described?
What is the behavior of components and how is it specified?
What is the execution and communication semantics of components?
What kind of data do they support?
What composition mechanisms are provided?
Which implementation technology is used?
Furthermore, component models are designed for a variety of application scenarios and different goals and are based on different assumptions about the hardware
and software platform.
In this chapter, we discuss the basic notions and problems that come up when we
want to build a software system from components. The discussion aims to substantiate the explanation of software components given in Section 1.2:
A software component is a part of a software system or an entity for composing
software systems. More precisely:
Software components have well-defined interfaces and a behavior that can
be described independent of the contexts in which they are used.
Software components have an implementation that can be independently
composed and deployed.
To be composable, software components follow a component model that determines the common computation, communication, and composition principles and rules underlying a class of components.
This explanation follows the definition given in [3], which is the most frequently
cited book on component software and which deals with concepts, frameworks,
and technology. [22] provides a large collection of articles also covering software
engineering aspects. [20] relates CBSD to traditional software development. A
book that is more implementation-oriented and covers implementation-related aspects of component frameworks is [4].

15

16

Chapter 2 Component Software

After presenting the learning objectives, this chapter looks at software systems
and their relationship to components (Section 2.2). As software systems are the
construction goal of CBSD, it is important to understand their basic aspects. Then,
we explain central notions of software components (Section 2.3). In particular, we
describe aspects such as component specification, implementation, and deployment. Section 2.4 presents different component models and communication mechanisms. Finally, we consider component infrastructures and central aspects of
component frameworks (Section 2.5). That is, we focus on the technical aspects of
component software in this part of the textbook. Development and engineering
aspects will be treated in Chapter 3.

2.1

Learning objectives of this chapter

After reading this chapter, you should


have a better understanding of what a software system is,
have learned the basic aspects and notions of components (specification, implementation, deployment),
understand the general aspects of component models,
understand the basic semantic issues underlying component models,
be able to compare component models and classify the differences,
understand the relationship between components, component infrastructure,
and component frameworks.

2.2

Software systems and component software

What is a software system? This question has several answers. Answers can, for
example, explain a system in terms of the program code. Or, they can consider
systems at runtime. In order to get a more precise understanding of software systems and their basic structures, we first consider installed or running systems. This
allows us to uniquely identify them, similar to physical objects. For example, we
can speak of the GNU Chess installation in certain files on a given computer as
being distinct from another installation on a different (or even the same) computer; or we can speak of the World Wide Web as a system installed on thousands
of computers. Similarly, we can identify running software systems by the corresponding processes and the system installation they are executing. Thus, like most
collections of physical objects, installed or running software systems have
an identity (in particular, we can say where the system parts are installed or
running);
a lifetime (from installation to uninstallation);

Chapter 2 Component Software

17

a state at each point in time (the state of a software system tells us what parts
of the system are running and comprises information about variables and the
state of execution);
a behavior.
In order to investigate and describe a system, we need to agree on the interfaces
separating the system from the context it is embedded in. As shown in Figure 2.1,
we distinguish two kinds of system interfaces: the interface between the system
and its platform (dashed line) and the interface between the system and its environment (solid line). The platform and environment of a software system have
different characteristics. That is why they play different roles in overall descriptions of systems.

Figure 2.1:
The two principle
interfaces of a
software system

Platform. Typically, a platform is a software/hardware system. The platform


provides the basic functionality on which a software system is built. For example,
the platform of an operating system is the computer hardware; the platform of an
Internet browser is the operating and window system as well as the network system providing TCP/IP; the platform of an e-business system includes browsers
and web servers. From these examples, it should be clear that it depends on the
point of view of whether system parts are regarded as parts of the platform or as
parts of the software system of interest.
In general, we assume that a software system SYS is built on top of its platform
PF in a layered way; i.e., SYS may use services from PF (e.g., by calling procedures of PF) and PF's general functionality (e.g., executing code). This implies
that SYS cannot be installed or run without its platform. Although a software sys-

18

Chapter 2 Component Software

tem is usually closely linked to its platform, there are good reasons to make the
distinction between a software system and its platform:
A platform may be used for several different software systems. Thus, it is reasonable to describe the platform separately and reuse it.
Separating the platform and the software system of interest allows focusing on
the relevant system parts.
During runtime, the user only interacts with the system and need not to be aware
of an underlying platform. There is one typical exception: In the case of terminating systems, the user usually starts the system by interacting with the platform.
System environment. A software system never operates in isolation. Typically, it
interacts with human users or other technical systems either via software or by
directly using sensors and actors. In particular, a software system can be a subsystem of a larger system. The interaction includes user inputs (keystrokes, mouse
clicks, text input, etc.), outputs for users (sound, text, paper, graphics, video, etc.),
messages, sensor values or calls from system parts in the environment, as well as
messages, output, or calls to such parts. As it is conceptually irrelevant whether a
system interacts with human beings or technical systems, we simply speak of the
environment of a software system to refer to the outside world it interacts with.
Whereas the communication of a software system with its platform is only needed
to understand its implementation, the interaction with the environment is crucial
for understanding the behavior of a software system. For example, in order to understand the behavior of a chess game with a graphical user interface, we have to
learn how to move the pieces and how to interpret the output from the system, but
we do not have to know how it allocates memory or how it uses the window too
kit.
Software systems. In order to provide a focused explanation, we spoke only of
single copies of installed or running software systems. It should be clear that
software systems are more general artifacts. In particular, they can be installed on
many different computers/platforms. As long as these platforms have the same
properties, the difference between designing a system for one installation or for
several installations is small. However, one often wants to design systems that can
run on different platforms. In these cases, the platform becomes a parameter of the
system. This parameter is instantiated at configuration or deployment time.
In practice, software systems have a number of implicit or explicit parameters.
The platform is just one example. Systems can be parameterized according to
their functional properties (e.g., an installation of a text processing system might
or might not include a spell checker). They may vary with respect to the size of
data or the number of transactions they can handle (e.g., database management
systems configured for a few gigabytes or hundreds of terabytes). They can be
configured for different system environments and users.

Chapter 2 Component Software

In summary, a software system is a technical system or subsystem in which software plays a dominant role. It should have a clear interface to its platform and to
the system environment. It may be parameterized for installation on different platforms, for providing different functionality, and for adaptation to different users
and environments. In particular, a software system has different stages. It can be:
1. designed, i.e., design specification of the system is completed;
2. programmed, i.e., the program code for the possibly parametric system is finished;
3. configured, i.e., prepared for deployment/installation;
4. deployed/installed, i.e., stored on a platform and ready for execution;
5. executing, usually in the form of processes on one or several computers/ platforms.
As we will discuss in the next paragraph, these stages are important for understanding the characteristics of component software.
Component software. The central idea of component software is to structure
software systems in such a way that its components are subsystems following
the same principles as complete systems. Putting it the other way round, software components are systems that we can compose. In particular, software components do not only have interfaces to other software components, but also interfaces to their platform and environment. Furthermore, the same five stages that we
have described for complete systems apply to each component:
1. Component specification including interfaces and behavior
2. Component implementation
3. Component configuration
4. Deployed/installed components
5. Running components, often called component objects or instances
It is important to distinguish between components and their instances at runtime,
just as it is important to distinguish between a class and an object. Nevertheless, to
simplify the wording, we will often take the liberty to speak of components where
component instances are meant.
The notion of component is closely related to the notion of composition. Different
component models provide different forms of composition. Central questions are:
1. What is the result of composition steps?
2. How is composition reflected in the above stages?
3. How is composition described; what are its mechanisms?

19

20

Chapter 2 Component Software

Concerning the first question, we can distinguish at least three different kinds of
composition:
Hierarchical composition: The result of the composition of components C1,
..., Cn is a new component C following the same model as the sub-components
Ci. From C and other components, further components can be built in a hierarchical manner. The software system to be developed is the root component of
the resulting hierarchy. Although this seems to be the natural way to go, it is
usually not supported in existing component frameworks.
Heterogeneous composition: The result of the composition of components
C1, ..., Cn is a new component C with different characteristics than the subcomponents Ci. For example, the result could be a complete software system
that has well-defined interfaces, has independently described behavior, allows
for encapsulation, is independently deployable, but the description and composition techniques for the whole system are different than for the subcomponents. This is the approach underlying many component frameworks.
Open composition: The result of the composition of components C1, ..., Cn is
just a web of components, that is, a set of connected components. The environment of such a component web has access to all interfaces of all subcomponents Ci. The component web does not satisfy the criteria of components.
Often these approaches are combined, in particular in connection with the second
question referring to the stages at which composition happens. For example, we
could support hierarchical composition during the specification and implementation stages and additionally allow open composition at runtime. These aspects and
answers to the third question above are the topic of Section 2.3.3.

2.3

Software components

To get a more substantial picture of what software components are and as a background for the following sections, we investigate the basic notions underlying
software components along with a small, but non-trivial example.
ThermoControl example. The ThermoControl example features a thermostat
with a user interface that controls an air conditioner. Figure 2.2 shows the four
components. The AirConditioner can be in three modes: OFF, HEATING, COOLING. The mode can be set by sending the message switchTo with the appropriate parameter. The ThermoSensor measures the current temperature in its physical
environment. The temperature can be read by the method getTemp.

Chapter 2 Component Software

21

Figure 2.2:
The components of
the ThermoControl
example

The Thermostat is a re-active component. Internally, it has two variables (variables are depicted by squares): desiredTemp records the desired temperature,
which can be modified from the outside by calling modifyDesiredTemp. The
parameter value is added to desiredTemp. The variable currentACMode
records the current mode of the AirConditioner. The Thermostat repeatedly reads
the current temperature, compares it to the desired temperature, adjusts the AirConditioner accordingly, and notifies the ControlPanel in case changes have occurred.

Figure 2.3:
A simple GUI for
the ThermoControl
system

The ControlPanel is the user interface component to increase or decrease the desired temperature. Its display shows the measured and desired temperatures as
well as the current mode of the AirConditioner. Whereas its interface to the
Thermostat is a programming interface, the methods incTemp and decTemp and
the three depicted variables are part of a graphical user interface modeling what

22

Chapter 2 Component Software

users can do. Figure 2.3 illustrates a possible layout of such a GUI. To indicate
that the user interface is not a programming interface, it is highlighted in gray.
In Figure 2.2, the components are arranged in a way that suggests how they are
supposed to be composed. Connections and communication between components
happens via so-called ports. Ports that provide an interface are depicted as circles
(provided ports). Ports that need to be connected to provided ports are depicted by
semi-circles (required ports). The connection of ports is explained and discussed
together with other composition aspects below.

2.3.1

Component specification

Specifications of component interfaces and behavior may be either informal or


more or less formal. As we consider software components as units that are used in
all stages from design to deployment, specifications have to reflect the implemented interfaces and the implemented behavior. This closely relates specifications to the chosen component model and, to some extent, to the implementation
technology. For example, the components of the ThermoControl example might
be based on a clock-synchronous component model or on object-oriented components in a multi-threaded execution environment (see Section 2.4 for explanations
of these models). The specifications become quite different depending on which
model is chosen.
As we do not want to assume that the reader is acquainted with specific formal
specification languages, we build our specifications here on the semantics model
underlying todays object-oriented programming languages and introduce notations as we go along (building on the prerequisites described in Section 1.5). In
particular, we use Java as a notation for types and interfaces, method calls for
communication, and multi-threading to express concurrency. For simple reference, we call this the Multi-Threaded object-oriented Component model or MTC
model for short.
Specifying components is a complex task. For the MTC model, there are generally
accepted specification techniques, but there is no overall modular specification
method that covers all difficulties caused by callbacks, thread interference, and
complex communication protocols. We describe here important specification
techniques and aspects in an informal way.
Interfaces and methods. As in Java, an interface declaration defines a type and a
list of method signatures. Interfaces are implemented by components. The interface declaration should specify the behavior that every implementation of the interface has to obey. For example, the interface implemented by the ThermoSensor
could be specified as follows:

23

Chapter 2 Component Software

interface ThermoSensorIF {
int getTemp()
// measures and returns current temperature;
// non-blocking;
// dependent on system environment;
// independent on component state;
}

Figure 2.4:
ThermoSensorIF

ThermoSensorIF is a typical API of a sensor device. The specification requires


that the method getTemp has to be implemented in such a way that it cannot
block and that its result only depends on the system environment and not on possible state information of the implementing component.
Components, state, interface extension. Similar to a Java class declaration, a
component declaration defines which interfaces the component implements and
provides the signatures of the component constructors. Figure 2.5 shows a declaration for the ThermoSensor.
component ThermoSensor implements ThermoSensorIF {
ThermoSensor() // creates a component instance
}
In the MTC model, components are similar to classes; in particular, they can implement multiple interfaces. However, there are central differences making components more general than classes. Whereas the instance of a class CL consists of
one object that has to provide the functionality for all interfaces implemented by
CL, the instance of a component CP might consist of several objects such that for
every interface implemented by CP, there is at least one object providing the needed functionality.
Component instances may have states. The current state of a component instance
can be modified by methods. State is specified by so-called model variables. The
state space of a component consists of all possible values that the model variables
can hold. States and model variables are abstract entities in the sense that they are
only used to specify method and component behavior; they need not be present in
the implementation.
We illustrate states, model variables, and further specification concepts by a sensor component ThermoSensor2, which records the maximal temperature that was
returned by getTemp. To specify the component, we extend the interface ThermoSensorIF by a model variable maxTemp and by refining the specification of
getTemp (Figure 2.6). The specification uses an ensures-clause. An ensuresclause consists of a Boolean expression that might relate the values of parameters,
the result, and the values of the model variables in the state before and after
method execution. The Boolean expression in the ensures-clause has to be true
after method execution.

Figure 2.5:
ThermoSensor

24

Chapter 2 Component Software

Figure 2.6:
RecThermoSensorIF

interface RecThermoSensorIF extends ThermoSensorIF {


model int maxTemp;
int getTemp()
// ... as in ThermoSensor
ensures maxTemp == max(result,old(maxTemp));
} }
The specification of getTemp in RecThermoSensorIF states in the ensuresclause that getTemp updates maxTemp to the maximum of the currently measured result and the old value, that is, the value of maxTemp before executing
getTemp. We allow such refinements as long as every implementation of the
refined interface is also an implementation of the original interface.
Of course, components implementing RecThermoSensorIF should also provide
methods for reading and resetting maxTemp. To achieve this, we could add further methods to RecThermoSensorIF. A slightly more flexible approach is to declare a separate interface for such methods (Figure 2.7):

Figure 2.7:
SensorHistoryIF

interface SensorHistoryIF {
model int maxTemp;
void setHistory( int startValue )
// only modifies maxTemp; non-blocking;
ensures maxTemp == startValue;
int maxMeasured()
// read-only; non-blocking;
ensures result == maxTemp;
}
The specification of SensorHistoryIF also illustrates other important properties of
methods in the context of stateful components. Methods can either have or not
have an effect on the state. Specifying that a method is read-only means that it
possibly reads the state, that is, it might dependent on the state, but it is not allowed to modify the state. Specifying that a method only modifies a certain part of
the state guarantees that other parts of the component state or of the component
environment are not effected.

Figure 2.8:
ThermoSensor2

component ThermoSensor2
implements RecThermoSensorIF, SensorHistoryIF
{
ThermoSensor2() // creates a component instance
}
Figure 2.8 shows a component declaration that makes use of the two interfaces
above. Similar to the treatment of methods in the context of multiple sub-typing in
Java, model variables with the same name are identified in the component; in par-

Chapter 2 Component Software

25

ticular, a component instance of type ThermoSensor2 has only one model variable
maxTemp. We also support component declarations with inlined interface and
model variable declarations. Figure 2.9 demonstrates this style by showing an
equivalent specification of the component ThermoSensor2. As the model variables have to be declared only once, this style is more convenient in cases with
complex state information.

component ThermoSensor2
implements RecThermoSensorIF, SensorHistoryIF
{
model int maxTemp;
interface RecThermoSensorIF extends ThermoSensorIF {
int getTemp()
// ... as in ThermoSensor
ensures maxTemp == max(result,old(maxTemp));
}
interface SensorHistoryIF {
void setHistory( int startValue )
// only modifies maxTemp; non-blocking;
ensures maxTemp == startvalue;
int maxMeasured()
// read-only; non-blocking;
ensures result == maxTemp;
}
ThermoSensor2() // creates a component instance
}
Types and data. In the context of component-based development, we can distinguish three kinds of types: data types, interface types, and component types. Data
types describe values that can be passed as parameters between components. We
assume that data types are passed by value. If mutable objects are used to represent data, these objects have to be copied when passed from one component to
another (similar to non-remote objects in Java RMI when passed to a remote
host). In particular, passing around data objects does not lead to new connections
between components. Usually, the primitive types of the used language are data
types (like int, boolean,...) or built-in types (like String). However, userdefined types may also be data types. A simple example is the type ACMode in
the ThermoControl scenario (see Figure 2.10).

enum ACMode { OFF, HEATING, COOLING }


component AirConditioner implements ACControlIF {
interface ACControlIF {
void switchTo( ACMode acm )
// switches this AirConditioner to mode acm
// non-blocking; effects system environment;

Figure 2.9:
ThermoSensor2-a

26

Chapter 2 Component Software

// independent on component state;


}
AirConditioner()

Figure 2.10:
AirConditioner

}
At least on the specification level of components, it is very helpful to clearly distinguish between data types describing values that are passed between components, interface types declaring the communication protocols offered by an object
or a channel, and component types bundling provided and required interfaces together to form executable entities.
Component activity. We distinguish between passive and active components in
the MTC model. A passive component provides methods that can be called, but
has no thread of its own. For example, ThermoSensor and AirConditioner are passive components, only providing APIs to a sensor and an actor device, respectively. Active components have at least one thread running in the component
and are the source of activities in component systems. The thread is either started
at creation time of the component instance or by calling a dedicated method. In
the ThermoControl example, Thermostat and ControlPanel are active components.
Being passive or active is part of the component specification. Active components
are marked with the keyword active. (In our graphical representations, active
components are depicted with a bold line (cf. Figure 2.2 )).
The behavior of active components is usually more difficult to specify, because
internal activity and calls from other components can happen concurrently. The
activity is specified together with the method or constructor that creates the
thread. As a first example of an active component, we discuss the specification of
Thermostat given in Figure 2.11.

active component Thermostat


implements ThermostatModifIF, RegisterIF
{
requires[1] ACControlIF ac;
requires[1] ThermoSensorIF ts;
requires[0..1] ControlPanelNotifIF cp;
model int desiredTemp;
model ACMode currentACM;
interface ThermostatModifIF {
void modifyDesiredTemp( int d )
// non-blocking; only modifies desiredTemp;
ensures desiredTemp == old(desiredTemp) + d;
}
interface RegisterIF {
void registerGUI( ControlPanelNotifIF cppar )
// non-blocking; only modifies cp;
ensures cp == cppar;

27

Chapter 2 Component Software

}
Thermostat(ACControlIF a, ThermoSensorIF t)
// constructor sets ac and ts and
// starts a nonterminating activity:
/* loop every x seconds {
int measuredTemp = ts.getTemp();
if( measuredTemp != desiredTemp &&
currentACM not appropriate
)
ac.switchTo( appropriate_mode );
if( (measuredTemp or desiredTemp or
currentACM have changed) and cp!=null )
cp.notify( measuredTemp, desiredTemp,
appropriate_mode );
*/
}
The component implements an interface to modify the desired temperature and to
register one control panel. It requires the connection to components implementing
the interfaces ACControlIF and ThermoSensorIF. The multiplicity [1] in
the requires-declaration states that these connections have to be established prior
to using the component and prior to starting its activity. That is why these connections are established by the constructor. The multiplicity [0..1] indicates that the
component implementing the interface ControlPanelNotifIF could be connected later. The rest of the specification should be self-explanatory. When studying the specification, three aspects should become clear:
1. Compared to class declarations, component specifications are more explicit
about their connections to other components. This simplifies the distinction
between internal state and external connections and makes it easier to check
design principles (for example that the connections are only done to interface
types and that components may not be used as method parameters).
2. Together with the specifications of the used data and interface types, specifications are self-contained.
3. Implementations of active components have to be careful regarding multithreading and synchronization. In the example, the activity thread of the
Thermostat runs concurrently with threads that call the methods modifyDesiredTemp and registerGUI and that modify parts of the shared component state.
active component ControlPanel
implements ControlPanelNotifIF
{
requires[1] ThermostatModifIF t;
provides[1] ModifyTempIF gui; // implicitly provided
visible int measuredTemp;
visible int desiredTemp;
visible ACMode controlLamp;

Figure 2.11:
Thermostat

28

Chapter 2 Component Software

user interface ModifyTempIF {


void incTemp() // represents a button
// calls t.modifyDesired(1) on klick
void decTemp() // represents a button
// calls t.modifyDesired(-1) on klick
}
interface ControlPanelNotifIF {
void notify( int mt, int dt, ACMode acm )
// non-blocking;
ensures measureTemp == mt;
ensures desiredTemp == dt;
ensures controlLamp == acm;
}
ControlPanel( ThermostatModifIF )
// activity corresponds to GUI event loop
// processing the button clicks of ModifyTempIF

Figure 2.12:
ControlPanel

}
Further specification aspects. In this short introduction to specification techniques for components, we could only consider basic aspects in an informal way.
In Section 2.4, we explain semantic properties related to component models in
more detail. Here, we like to discuss further aspects to extend the expressivity of
the sketched specification techniques:
We claimed that there is a clear distinction between value, interface, and component types. A more comprehensive specification technique has to establish
the relationships between objects of these types and has to provide constructs
for handling composed date types and expressing data abstraction.
We encourage the consideration of user interfaces as components and specifying their behavior with the technique that we have sketched so far. Figure
2.12 illustrates such a specification for the ControlPanel component. The buttons that the GUI provides are modeled as an interface. This interface is implicitly provided by the component. Furthermore, the variables marked as
visible are supposed to be visible in the view of the GUI.
In the examples above, the object created when a component is instantiated
implements the provided interfaces of the component. A slight exception is the
ControlPanel component, as the object of the component type ControlPanel
does not implement ModifyTempIF. In general, components should be able
to provide interfaces dynamically. This can be handled in the specification
technique by methods returning object references of an interface type (that is
not a value type).
Although we have been explicit about concurrency issues (there is a lot of
work about components that avoids this crucial topic), we have not discussed
synchronization and more complex communication aspects. For example,
callback scenarios are difficult to handle in specifications. A callback sce-

Chapter 2 Component Software

nario occurs when a component CP1 calls a method m implemented by a


component CP2 and when the execution of m leads to call to a method of CP1.
At the end of these introductory explanations, we would like to again stress the
role of component specifications. They provide an implementation-independent
contract between users and providers of components. Users do not have to look at
the implementation to understand the components behavior and know whom to
blame if a component implementation does not behave as specified. Users can
choose between components of different providers competing for the best implementation or the best price. Providers can modify and improve their implementations as long as the specification is not affected.

2.3.2

Component implementation

As components are meant for composition, the implementation of a component


CP has to take into account the component model and the implementation technology of those components with which CP should be composable. Furthermore,
it might use common data types and declarations of required interfaces. To illustrate these aspects, we consider the implementation of the component Thermostat.
As the ThermoControl example is based on the MTC model, it is natural to implement its components using a typed object-oriented language. We choose Java,
but we could have chosen, for example, C# instead. Whereas the choice is not so
important from the programming perspective, it makes a big difference when it
comes to deployment and composition with existing components.
interface ThermostatModifIF {
void modifyDesiredTemp( int d );
}
interface RegisterIF {
void registerGUI( ControlPanelNotifIF cppar );
}
class Thermostat
implements Runnable, ThermostatModifIF, RegisterIF
{
ACControlIF
aircon;
ThermoSensorIF sensor;
volatile ControlPanelNotifIF guicp;
volatile int desiredTemp;
ACMode currentACM;
int currentTemp;
volatile boolean doUpdate = false;
Thermostat( ACControlIF ac, ThermoSensorIF ts ) {
aircon = ac;
sensor = ts;
desiredTemp = 20;

29

30

Chapter 2 Component Software

currentACM = ACMode.OFF;
new Thread(this).start();
}
public void registerGUI( ControlPanelNotifIF cp ) {
guicp = cp;
doUpdate = true;
}
public void run() {
while (true) {
int newTemp = sensor.getTemp();
if (newTemp != currentTemp) {
currentTemp = newTemp;
doUpdate = true;
}
if (desiredTemp < currentTemp &&
currentACM != ACMode.COOLING) {
aircon.switchTo(ACMode.COOLING);
currentACM = ACMode.COOLING; doUpdate = true;
} else if (desiredTemp > currentTemp &&
currentACM != ACMode.HEATING ) {
aircon.switchTo(ACMode.HEATING);
currentACM = ACMode.HEATING; doUpdate = true;
} else if (desiredTemp == currentTemp &&
currentACM != ACMode.OFF ) {
aircon.switchTo(ACMode.OFF);
currentACM = ACMode.OFF; doUpdate = true;
}
if( doUpdate && guicp != null ) {
guicp.notify(currentTemp,desiredTemp,currentACM);
}
doUpdate = false;
try {
Thread.sleep(1000);
} catch( InterruptedException e ) {}
}
}
public void modifyDesiredTemp( int t ) {
desiredTemp += t;
doUpdate = true;
}

Figure 2.13:
ThermostatImpl

Figure 2.13 shows a Java implementation for the Thermostat component (access
modifiers are left out wherever possible). The component is realized as a class.
The connections to the required interfaces and the model variables are represented
by fields. The (re-)active behavior of the Thermostat is implemented by a component-local thread that measures the temperature every second, updates the AirConditioner accordingly, and notifies the ControlPanel if a change has occurred.
The thread is started after the initialization of the strictly required connections. As
the multiplicity of the ControlPanel is zero or one, we can start the thread at the
end of the constructor and register the ControlPanel later.

Chapter 2 Component Software

According to the overall usage scenario as depicted in Figure 2.2 and the specification of the Thermostat, there are at least three threads that can modify the state
of a Thermostat, namely the Thermostats own thread, the thread of the ControlPanel (possibly modifying the desired temperature), and the thread that registers
ControlPanels (possibly modifying the field guicp). To synchronize the modifications of the fields between the threads, the fields are declared as volatile.
Without such a declaration, it could happen according to the Java semantics
that a modification by one thread remains invisible to others. Of course, in more
complex component scenarios, more sophisticated synchronization is needed, in
particular to avoid deadlocks.

2.3.3

Component composition

We approach the different aspects of component composition by discussing the


following, almost orthogonal questions:
1. What is composed?
2. What is the result of the composition?
3. How is the composition described?
4. When does the composition happen?
After the discussion of these questions, we illustrate some of the aspects using our
ThermoControl example. Details related to other aspects will be handled together
with component frameworks in later chapters of the book.
What is composed? Composition can be done on the level of the component descriptions or on the level of component instances. In the first case, component
specifications or program code is composed, in the second case runtime entities
like objects or processes are composed. Techniques on the description level range
from abstract composition operations on specifications
and composition of source code (for example using macro expansion mechanisms or invasive composition techniques ([6])
via static linking of assembler code
to dynamic loading and linking of executable code.
If not done manually, composition on the description level is usually performed
by tools like compilers, linkers, or the runtime system.
Composition on the instance level typically happens at runtime by making variable names or references of one component instance available to another component instance. If instance level composition happens within one process, it presupposes some form of code composition. If component instances of different

31

32

Chapter 2 Component Software

processes or on different platforms are composed, direct code composition is not


needed, but some form of integration with a communication infrastructure.
What is the result of the composition? As described in Section 2.2, we distinguish three kinds of composition: 1. Hierarchical composition: If applied on the
code level, it should result in the code of a new component according to the same
component model. If applied to component instances, it should result in a new
component instance according to the same component model. 2. Heterogeneous
composition: Here, the situation is similar to the hierarchical case, but the resulting components and component instances follow a different component or system
model, often with less flexibility and less support for further composition. 3. Open
composition yields a web of connected instances without a dedicated interface to
the environment.
How is the composition described? The composition of code is usually described by scripts for configuration, building, or deployment tools or is simply
done by hand. Component frameworks support code composition with standardized mechanisms for deployment/installation. The composition of instances is
mostly done by the component constructors or additional program code, often
called glue code. The glue code can do the composition of instances locally in one
process or remotely by using communication mechanisms. Component frameworks support instance composition with registration and lookup facilities.
When does the composition happen? Composition on the description level can
be done at all stages (stages are described at the end of Section 2.2) from specification to runtime. Composition on the instance level happens at runtime, but the
descriptions that manage these compositions (glue code, composition rules, etc.)
may again be related to different stages.
Illustrating composition. The discussion above sketched a variety of possibilities
for composition. It is beyond the scope of this introduction to illustrate all of
them. But, at least some typical aspects should be demonstrated along with the
ThermoControl example. We consider local composition of the components, construction of a new component from the given components, and a scenario with
distributed composition.

Figure 2.14:
ThermoControlGlu
eCode

ThermoSensorIF s = new ThermoSensor();


ACControlIF
a = new AirConditioner();
Thermostat
t = new Thermostat(a,s);
ControlPanelNotifIF c = new ControlPanel(t);
t.registerGUI( c );
The local composition of the four components according to the design of Figure
2.2 can be described by the glue code of Figure 2.14. In the first two lines, we
create the ThermoSensor and the AirConditioner and record the provided ports in
the variables s and a. The composition with the Thermostat is done in the con-

Chapter 2 Component Software

33

structor of the Thermostat component. This is possible because there are no mutual dependencies. The composition of the Thermostat and the ControlPanel cannot be done within the constructors, because these components have a mutual dependency. The solution for their composition is asymmetric. Connecting the required port of the Thermostat to the ControlPanel is done in the constructor of the
ControlPanel, whereas the ControlPanel is connected to the Thermostat by the
method registerGUI. As we need both ports of the Thermostat, variable t
cannot be of the interface types provided by the ports, but has to be of the component type.
The example demonstrates composition on the instance level and yields a web of
four components. In particular, the result of the composition is not a component
by itself. The glue code describes a composition at runtime. Even in this simple
example, we can observe aspects of non-uniform composition descriptions: The
components ThermoSensor and AirCondition are only handled by one of their
provided ports, whereas for Thermostat we had to use the implementation type.
Most compositions are performed by the constructors, but one connection had to
be established by a special method. Another source of non-uniformity often comes
from the different possibilities to start components' activities. In the example, the
activities are started by the constructors of Thermostat and ControlPanel. Often,
the startup has to be done explicitly by the glue code after the composition is
completed (note that Thermostat is started before that). As non-uniform composition is not feasible for component frameworks, they usually define a standard for
composition and require that components provide special interfaces for composition (similar to RegisterIF) and startup (see Chapter 4 for composition standards and Chapter 6 for automated composition techniques and dependencies injection).

Figure 2.15:
The
ThermoControl
system composed
out of the four
components

34

Chapter 2 Component Software

To illustrate the construction of new components from given ones, that is, hierarchical or heterogeneous composition, we specify a new component TCSystem,
which encapsulate the complete thermo-control system and only provides the user
interface. Figure 2.15 depicts the overall component. Figure 2.16 provides the
specification for that component. As it is often the case for composed systems, the
overall behavior is explained in terms of the subsystems, even if the subsystems
are encapsulated. The implementation of the components consists of the code for
the constructor TCSystem, which is identical to the glue code of Figure 2.14. In
general, the implementation of a composed component CP consists of glue code
for composing the subcomponents and of code realizing additional functionality
of CP.

active component TCSystem


{
provides[1] ModifyTempIF gui; // implicitly provided
visible int measuredTemp;
visible int desiredTemp;
visible ACMode controlLamp;
user interface ModifyTempIF {
void incTemp() // represents a button
// increments desired temperature by one
void decTemp() // represents a button
// decrements desired temperature by one
}
TCSystem() // provides a user interface for a
// thermo control system composed from an
// AirConditioner, ThermoSensor and a Thermostat

Figure 2.16:
TCSystem

}
To illustrate further composition aspects, we consider a distributed scenario where
the ControlPanel and the Thermostat run on different devices. Again, we first look
at the Java implementation and then discuss related requirements for component
frameworks. Unfortunately, we cannot use the component implementations as
they are, but have to modify their code to make them fit for distribution. All provided interfaces of the components have to implement the Java library type Remote; a number of additional exceptions have to be handled; and the components
have to be extended or wrapped to be accessible remotely. Note that these changes
can cause further modifications.

try {
String tsurl = ... // URL of local device
String cpurl = ... // URL of ControlPanel device
boolean connected = false;
ThermoSensorIF s = new ThermoSensor();

Chapter 2 Component Software

ACControlIF
a = new AirConditioner();
Thermostat t = new Thermostat(a,s);
ControlPanelNotifIF c = null;
Naming.rebind(tsurl+"thermostat", t );
do {
Thread.sleep(1000);
try {
c = (ControlPanelNotifIF)
Naming.lookup(cpurl+"controlpanel");
connected = true;
} catch( NotBoundException nbe ) {
System.out.print("Binding failed; try again");
}
} while ( !connected );
t.registerGUI( c );
} catch( Exception ioe ) {
System.out.println("Connection failed"); ...
}

More substantial are the changes to the glue code, in particular because the considered components ControlPanel and Thermostat are mutually dependent. Figure
2.17 presents the glue code for the platform of the Thermostat. After the creation
of the three local component instances, the Thermostat instance t is registered at
the local name server (rmiregistry) under the name thermostat. Then, a loop is
started that tries every second to look up the remote object of the ControlPanel in
the remote registry under the name controlpanel. If the lookup is successful, the
remote port object c is registered at t. The glue code for the ControlPanel device
is similar.
Component frameworks should make it easy to go from local execution to distributed scenarios. They should automatically generate the needed implementation
parts or provide an appropriate infrastructure. Furthermore, they should allow migrating components at runtime, for example for load balancing in computationally
intensive application scenarios. To achieve support, component frameworks have
to fix design decisions concerning the component descriptions and the implementation technology. We will discuss solutions to these problems in later chapters of
the book.

2.3.4

Deployment

Deployment essentially means bundling the program code of the components and
possibly the glue code into deployable units and making these units available on
the target platforms. Deployment depends on many technical aspects, in particular
on the implementation language, the configuration techniques, and the platform
properties. Here, we focus on the aspect that is most specific for CBSD, namely
the bundling of program code to deployable units. As our examples are based on
Java technology, it is clear that the deployable units are bundled as jar-archives
and that they are usually made available on a platform by copying them into a di-

35

Figure 2.17:
ThermoControlRe
moteGlueCode

36

Chapter 2 Component Software

rectory that is in the class path of the executing virtual machine. And following
the principle that components are independently deployable, there has to be at
least one bundle for each component.
The central question remaining is: What belongs to a code bundle of a component? Unfortunately, this question has no simple and general answer. Of course,
the bundle for a component contains the class that implements the component. For
example, the bundle for the Thermostat contains the class Thermostat. But, already for the interfaces ThermostatModifIF and RegisterIF, it is less clear if they
should be part of the bundle. There might be other components also implementing
these interfaces, so that the assignment of the interface declarations to the bundle
of only one of these components seems inappropriate. However, if we place the
declarations into several bundles, we have to make sure that they are considered to
be the same if more than one of these bundles is used. In a first approach, we distinguish the following six code categories in an implementation CPI of a component CP and sketch how they could be handled:
1. Library code: A CPI might use standard or generally available library or
framework code. This should be made available on the platform to all components and need not be part of CPIs bundle. (E.g., the components of the
ThermoControl example used Java library code from the io-, swing-, and rmipackages)
2. Dedicated component code: The core part of a CPI consists of code that is
only used by this component. It should be encapsulated and is definitely part
of CPI's bundle. (E.g., the code of the component constructors)
3. Code of subcomponents: Subcomponents have their own code bundles. Here,
a decision has to be made about whether the code bundles of subcomponents
should be deployed separately or whether they should be part of the composed
component's bundle. Depending on the component system, both options could
be reasonable.
4. Shared hidden code: Different components (of a special domain) might share
implementation parts that are not component implementations and are not
generally available on the platforms. As in item (3.), different options are possible and reasonable.
5. Dedicated interfaces and data types: Interface and data types that are visible
in the interface of a component CP and that are only implemented by CPI
should be part of the CPI bundle.
6. Shared interfaces and data types: Interface types that are implemented or
extended by several components, and data types for complex values that are
interchanged between a number of components, should be kept in separate
header bundles. (E.g., interface type ThermoSensorIF is used in component
ThermoSensor and ThermoSensor2.)

Chapter 2 Component Software

The simple bottom line of the discussion of the code categories is that there is no
direct mapping from code parts to components. Components usually share code
and this sharing has to be managed during the build and deployment process. In
addition to this, deployment has to take care of the glue code in open composition
settings.

2.4

Component models

To be composable, components have to agree on general interface and behavioral


aspects. In particular, the communication mechanisms at component boundaries
have to match. Component models determine the common computation, communication, and composition principles and rules underlying a class of components. In addition, a component model can describe other common aspects of its
components, for example the available reflexivity mechanisms supported by the
components. Components adhering to different component models are not prepared for composition. For example, multi-threaded object-oriented components
cannot be smoothly composed with components assuming a clock-synchronous
behavior and communicating via shared variables. In this section, we consider
four different component models:
1. MTC: Multi-threaded components
2. ACC: Asynchronously communicating components
3. SCC: Synchronously communicating components
4. CSC: Clock-synchronous components
All models are widely used in practice. The acronyms, however, are non-standard
and are introduced here for ease of reference. Each component model is of interest
on its own. Putting them side by side helps to further sharpen our understanding
of component behavior and composition. Before we look at each of the models,
we take a general look at component behavior and communication.

2.4.1

Component behavior and communication

Figure 2.18 demonstrates the central parts of component instances at runtime. A


component instance has a component type, a memory, a set of activities, and a
number of connectors to the environment. An activity is similar to a thread, task,
or process; it can be active or suspended. Each activity can perform so-called actions.

37

38

Chapter 2 Component Software

Figure 2.18:
A component in
stylized notation

Typical actions are:


read or modify the memory
send a message to a connector
create a connector or connect to a connector
create a new component instance
suspend, resume, or delete an activity
Activities never work within other component instances. In particular, they cannot
perform actions that directly affect other component instances. A connector is an
entity that connects components. For example, a port is a connector. Other connectors will be explained below.
The general procedure to execute a component system is to select those active activities that should be executed next and to let each of these activities perform a
finite number of actions. With such a step, the states of the component instances
and connectors can change in many ways and new component instances can be
added or deleted.
Component models restrict the possible behavior of component systems by defining in particular:
the kinds of connectors that can be used;
the mechanism for components to be connected to connectors;
the number of activities that are allowed to be active in a component instance
and their actions;

Chapter 2 Component Software

the mechanism for selecting the next activities to be performed.


Component models also define what kind of composition is supported (hierarchical, heterogeneous, or open). In the following subsections, we only consider open
composition and concentrate on the computation and communication aspects. For
each of the models, we explain how the ThermoControl example can be handled
within the model and sketch advantages and disadvantages of the model. To keep
the changes to the above ThermoControl version small, we stay with the objectoriented paradigm and assume that the memory of each component consists of
objects and their states. Nevertheless, it should be clear that the same models can
be realized in other language paradigms as well (we will provide references for
further reading).

2.4.2

MTC: Multi-threaded components

In MTC, communication between components is realized by threads that cross the


boundaries of component instances. For example, the thread of the Thermostat
component calls the method notify of the connected ControlPanel component
instance CP (see Figures 2.2 and 2.13). The control leaves the Thermostat component, executes code that belongs to the implementation of CP, and performs updates to variables in CP. As ControlPanel itself is an active component, there are
now two threads in CP executing concurrently. This may lead to data races and
can break implementation invariants.
The general picture is even worse. Every call on an interface method can lead to
an additional thread executing within the boundary of the component, i.e., read
and modify objects, create new objects, etc. Furthermore, a thread coming from a
component instance C1, now executing in C2, can leave C2 to call a method in C3
and then call back on C2 before returning from C2 to C3, then to C2 and to C1.
To avoid the sketched problems of multi-threading, the component designer has to
realize and implement a synchronization discipline that guarantees thread safety.
This is already non-trivial in scenarios where the designer knows all components
in the context. It becomes more difficult in scenarios (with possible call backs)
where the components in the context are unknown.
If we express component-crossing threads by our conceptual notion of local activities, the complexity is more visible. A thread is split up into several activities.
A call of an interface method in another component C2 suspends the local activity
A1 and creates a new active activity A2 in C2. A2 might create further activities.
On termination of A2, it resumes activity A1. This shows in particular that without appropriate synchronization, a component has no control over who creates activities inside itself.
The advantage of MTC is that it has very efficient implementations and is a
straightforward generalization of sequential code. The disadvantage is that the

39

40

Chapter 2 Component Software

concurrent behavior of MTC components can be difficult to specify. Furthermore,


thread safety is difficult to achieve (see the discussion on thread safety in [19],
Section 2.1).

2.4.3

ACC: Asynchronously communicating components

A better separation of components can be achieved by communicating via messages. Asynchronous messages are like emails sent by the sender component CS
to the receiver component CR. After sending the message, the sender can immediately continue with its activity, not waiting for any synchronization with the receiver. At the receiver site, a message queue records the incoming messages. The
receiver component can decide in which order or whether at all it likes to act to
received messages. In the ACC model, all components are active as they are supposed to read and react to incoming messages.

Figure 2.18:
The
ThermoControl
example in the
ACC model

Figure 2.18 demonstrates a realization of the ThermoControl example based on


the ACC model. As indicated by the bold surrounding lines, all components are
active. The components have at least one message queue, indicated by the hatched
connector symbol that is connected by a bold line to the component. Components
sending to a queue are connected by a thin line with a half circle to the corresponding connector. Messages consist of a name and an optional list of parameters. Each queue is annotated with the messages it accepts. For example, the ControlPanel accepts at one of its queues the messages IncTemp and DecTemp, and
at the other the message Notify with the same parameters as the notify method
in the MTC version. Depending on the message kind
enum MsgKind { MODIFYDESIREDTEMP, CURRENTTEMP }

Chapter 2 Component Software

the message TempMsg contains the offset for modifying the desired temperature
or the current temperature measured by the ThermoSensor.
Interface methods with return values are split into two messages. For example, the
message getTemp is realized by the message GetTemp, which requests the
ThermoSensor to send the current temperature. The ThermoSensor is expected to
answer with a message TempMsg(MsgKind.CURRENTTEMP,x), where x is
the measured temperature value.
Implementation. The typical implementation pattern of a component in the ACC
model consists of a loop that reads from the incoming message queue and reacts
to the messages by modifying the internal state and sending messages. In particular, the thread(s) running in such components never leave the component. Their
activities remain local. Synchronization between components is handled by the
queues. Component implementations do not need to worry about this.
class Thermostat extends LinkedBlockingQueue<TempMsg>
implements Runnable {
BlockingQueue<Notify> tocp;
BlockingQueue<GetTemp> tots;
BlockingQueue<SwitchTo> toac;
int desiredTemp = 20;
ACMode currentACM = ACMode.OFF;
boolean doUpdate = false;
...
public void run() {
int currentTemp = 0;
TempMsg currmess;
while (true) {
try {
tots.put(new GetTemp()); // ask for the temperature
currmess = take();
while(currmess.mname==MsgKind.MODIFYDESIREDTEMP){
desiredTemp += currmess.tempval;
currmess = take();
}
assert currmess.mname == MsgKind.currentTemp;
int newTemp = currmess.tempval;
if (newTemp != currentTemp) {
currentTemp = newTemp;
doUpdate = true;
}
if( desiredTemp < currentTemp &&
currentACM != ACMode.COOLING) {
toac.put(new SwitchTo(ACMode.COOLING));
currentACM = ACMode.COOLING; doUpdate = true;
} else if( ... ) {
...
}
if (doUpdate) {
tocp.put( new Notify(currentTemp,
desiredTemp, currentACM));

41

42

Figure 2.19:
ThermostatImplAC
C

Chapter 2 Component Software

}
doUpdate = false;
Thread.sleep(1000);
} catch (InterruptedException e) {}
} } }
...
}

To illustrate this pattern, we consider a fragment of a possible implementation of


the component Thermostat based on the ACC model (see Figure 2.19). For the
implementation, we use the interface type BlockingQueue from the package
java.util.concurrent as a message queue interface. We only need the
methods put for adding a message to the queue and take for retrieving a message from the queue. If the queue is full, the method put blocks until it can add
its parameter. Instead of directly connecting to objects of other components as in
the MTC model (cf. Figure 2.13), in the ACC model the components only communicate via the queues. For example, Thermostat has one input queue and three
output queues tocp, tots, and toac, which connect it to the ControlPanel, the
ThermoSensor, and the AirConditioner, respectively. Here, we assume that the
input queues are part of the components that read them (indicated by a bold line
between components and their input queues in Figure 2.18). That is why we could
realize Thermostat by inheriting from the class LinkedBlockingQueue, which we
use as queue implementation.
The run method in Figure 2.19 looks similar to the one of the MTC model in Figure 2.13. However, as the ACC model provides less synchronization between the
components, Thermostat has to take into account the possible incoming message
sequences. It first asks for the current temperature and then reads from its input
queue. As long as it receives messages from the ControlPanel, it updates the desired temperature. After receiving an answer from the ThermoSensor, it proceeds
as in the MTC model, except that it sends messages to AirConditioner and ControlPanel instead of calling methods.
Discussion. In the ACC model, the message queues play the role of connectors
between components. Connectors can be realized as parts of components. In this
case, connectors are created together with component instances or by the components. This is the option that we demonstrated here together with the ThermoControl example. Another option is to create the connector instances with the glue
code and then connect connectors and component instances. That option will be
demonstrated in the next subsection. In general, both options can be combined
with both models. However, most realizations of the ACC assume that the input
message queue is allocated together with the component reading from it. In particular in distributed settings, this is advantageous.
The advantage of the ACC model over the MTC model is that problems of data
races and thread safety between components are avoided. All these low-level synchronization aspects are either handled in a generic way by the queues or they remain local to components. In particular, call-back scenarios are treated in a more

Chapter 2 Component Software

natural way. Furthermore, the component has better control over incoming messages. It can decide when or whether at all it wants to react to messages. This is
more difficult to achieve in the MTC model. The disadvantage of the ACC model
is that the indirection caused by using messages and queues often reduces efficiency and requires that the programmer explicitly distinguish between messages
and methods. In addition to that, the connection between call and return simplifies
to handle closer coordination between components.
As Java concurrency is based on multi-threading, the realization of ACC in Java
looks a bit unnatural. Other languages and some Java extensions support ACC
directly. Maybe the most prominent language based on the ACC model is Erlang
([7]). Languages like Java support implementations according to the ACC model
with classes in the library. The Java Enterprise Edition provides the Java Message
Service (JMS) for communicating via asynchronous messages. Furthermore, there
are a number of Java extensions with built-in constructs for asynchronous communication (see, e.g., [11] and [37]).

2.4.4

SCC: Synchronously communicating components

With asynchronous messages, it is difficult to achieve closer synchronization


among components. For instance, the following behavior of a scenario with one
producer and one consumer is not easily realizable in the ACC model: The producer has a loop in which she creates a product, waits until the consumer has accepted the product, and then continues with the next iteration of the loop. The
consumer has a loop in which he waits until a product is available, accepts and
consumes it, and then continues with the next iteration of the loop. The SCC
model directly supports such synchronizations or rendezvous. Messages are only
transmitted if both sender and receiver are ready to perform the communication. If
only one of them is ready, the other one is blocked.
Figure 2.20 demonstrates a realization of the ThermoControl example based on
the SCC model. The filled connector symbols denote channels for synchronous
communication: The activities of sender and receiver components are synchronized. For example, if Thermostat wants to send a Notify message, it is
blocked until an activity in ControlPanel is ready to accept the message. On the
other hand, if ControlPanel is ready to accept a message at that channel and
Thermostat is not sending, ControlPanel is blocked. At a channel with two inputs,
like the channel at which Thermostat receives the temperature messages, the receiver has to be prepared to accept messages from both senders.

43

44

Chapter 2 Component Software

Figure 2.20:
The
ThermoControl
example in the
SCC model

The SCC model simplifies the synchronization of components. On the other hand,
the risk of deadlocks is higher in SCCs than in ACCs. A typical deadlock arises if
a component C1 blocks because it waits to receive a message from component C2
while C2 blocks waiting to receive some message from C1. To show the absence
of deadlocks, it is helpful to abstract the component behavior into so-called interface automata. Interface automata are also helpful as a specification technique for
synchronously communicating components.
An interface automaton is a finite automaton without final states. Transitions in
interface automata are labeled with message names suffixed by ? for receive
messages and by ! for send messages. In addition, the label may be used to
indicate an internal transition of the component. An interface automaton abstracts
from the internal behavior and the data manipulation of a component. Interface
automata were introduced in [5] to precisely study an optimistic approach to composition.
Figure 2.21 demonstrates interface automata along with the ThermoControl example. Messages are abbreviated as shown in the upper left corner of the figure.
Channels are annotated with the messages they allow. The direction of message
flow is the same as in Figure 2.20. The behavior of each component is modeled by
an interface automaton. The states of the automata are depicted by small circles;
starting states are indicated by a short arrow (without a source state). -labeled
transitions may always be taken. A transition with a receive message label m? in a
component Crcv may only be taken if
Crcv is a receiver at a channel for m and

45

Chapter 2 Component Software

there is a component Csnd that is connected as a sender to the channel and is in


a state with an outgoing edge labeled by m!.
The transition is synchronously taken in both automata and leads to the successor
state for the corresponding label. Thus, each non- -transition has a corresponding
transition in the automaton of another component.

Figure 2.21:
acInterfAutom

Implementation. A simple way to implement synchronous communicating behavior in object-oriented languages is by using appropriate channel classes instead
of message queues. For example, Java provides the blocking queue class SynchronousQueue<E> in the package java.util.concurrent, in which each putoperation must wait for a corresponding take-operation, and vice versa. A synchronous queue does not have any internal capacity, not even a capacity of one.
Thus, it can be used as a synchronous channel.
BlockingQueue<Notify>

inchCP = new SynchronousQueue<Notify>();

BlockingQueue<TempMess> inchTh = new SynchronousQueue<TempMess>();


BlockingQueue<ACMode>

inchAC = new SynchronousQueue<ACMode>();

BlockingQueue<TempMess> inchTS = new SynchronousQueue<TempMess>();


ThermoSensor

s = new ThermoSensor(inchTS,inchTh);

AirConditioner a = new AirConditioner(inchAC);


Thermostat t = new Thermostat(inchTh,inchCP,inchTS,inchAC);
ControlPanel c = new ControlPanel(inchCP,inchTh);

Figure 2.22:
ThermoControlSC
CGlue

46

Chapter 2 Component Software

Whereas we assumed in the last subsection that the input queues are part of the
components that read them, we demonstrate here how connecting is done in a scenario in which the connectors/channels are provided by the glue code describing
the composition. For our running example, the creation of channels and component instances can be realized by the code fragment of Figure 2.22. The component constructors take the channels as parameters and establish the connections.
The method implementations in the SCC version of the classes are almost identical to the ones of the ACC version. However, this is not a typical case. In other
scenarios, implementations can differ a lot between asynchronous and synchronous versions.
Discussion. Like the ACC model, the SCC model achieves a better separation of
components than the MTC model. The channels play the role of connectors between components. Compared to ACC, the SCC model simplifies the coordination
between the activities in the different components. This is one of the main reasons
why many calculi for concurrency use synchronous communication, in particular
CSP (communicating sequential processes, see [24]), CCS (calculus of communicating systems, see [30]), and the -calculus (see [31]). A disadvantage of this
closer cooperation is the higher risk of deadlocks.

2.4.5

CSC: Clock-synchronous components

In clock-synchronous concurrency, the components process in lockstep with a


central clock. Each clock cycle consists of two steps. In the first step, all components read inputs from the output variables of other components (read step). In
the second cycle, the components perform their internal computation and write
their own output variables (compute step). Thus, synchronization among components is handled by a central mechanism.
Clock-synchronous concurrency is very often used in embedded systems (see [1]
for a thorough treatment of CSC for embedded systems). Special programming
languages, so-called synchronous languages, are used to develop clocksynchronous systems. As the discussion of synchronous languages is beyond the
scope of this book, we use a coarse simulation of their behavior in Java. We assume that every component has two methods: a method readStep for the read
step and a method computeStep for the compute step. In the read step, the underlying system executes the readStep methods of all components in parallel. In
the compute step, the computeStep methods of all components are executed. It
should be clear that this technique is only meant to help explain the CSC model. It
is not a realistic implementation option.

47

Chapter 2 Component Software

Figure 2.23:
The
ThermoControl
example in the
CSC model

To realize the ThermoControl example based on the CSC model, we provide each
component with variables for output. These variables are read from other components in the first step of a clock cycle. We assume here that reading is done by a
method call. Of course, we could have allowed direct access as well. For example,
ControlPanel has a variable desiredTemp and provides a method readDesiredTemp. Figure 2.23 gives the complete picture for the ThermoControl example.
public void readStep() {
desiredTemp = guicp.readDesiredTemp();
measuredTemp = sensor.readTemp();
}
public void modifyStep() {
if(
desiredTemp < measuredTemp &&
currentACM != ACMode.COOLING) {
currentACM = ACMode.COOLING;
} else if( desiredTemp > measuredTemp &&
currentACM != ACMode.HEATING ) {
currentACM = ACMode.HEATING;
} else if( desiredTemp == measuredTemp &&
currentACM != ACMode.OFF ) {
currentACM = ACMode.OFF;
}
}

It is interesting to compare the needed functionality of the read step and the compute step of the Thermostat component with the run method of the implementations according to the MTC and the ACC models (see Figures 2.13 and 2.19

Figure 2.24:
ThermostatImplCS
C

48

Chapter 2 Component Software

resp.). As Figure 2.24 illustrates, the functionality is reduced to the core behavior.
All coordination is implicitly managed by the clock-based synchronization.
Discussion. The CSC model is mostly used in areas where software systems are
developed for dedicated hardware, like in embedded system development. If the
machine clock can be used to realize the virtual clock underlying the CSC model
and if the concurrent components can be placed on concurrently executing processors, system implementations according to the CSC model are very time- and resource-efficient. Furthermore, the CSC model simplifies static analysis because
the timing behavior is easier to predict and because it has less sources of nondeterministic behavior than the other models in which the (implicit) scheduler has
a strong influence on the execution.
The CSC model is less appropriate in distributed settings with communication delays and for applications in which the computation time needed by different components for one step varies considerably, or in which complex data has to be communicated between components. Another disadvantage of the CSC model is that it
is less clear how to use it together with software according to the other models.

2.5

Component infrastructure and framework support

Component models determine the common computation, communication, and


composition principles and rules underlying a class of components. Following a
component model restricts the flexibility of the software development. On the
other hand, the component model provides the basis for separate development,
simplifies integration and composition, and enables the use of development infrastructures dedicated and specialized for the model: The component infrastructure for a component model CM comprises all conceptual and technical support
to build, execute, and manage software systems from components following CM.
The following list presents typical goals, aspects, and tools that might be supported by component infrastructures:
Language support for specification, implementation, composition, and deployment.
Mechanisms and standards supporting introspection of components at runtime;
this is in particularly important for dynamic composition.
Support for certain tasks that many components share or that cross component
boundaries, for example support for user interfaces, for transactional behavior,
or for realizing persistence; such support can be provided, for instance, in the
form of libraries or services.
Rules and conceptual support to statically build new components from existing components (for example in the context of hierarchical composition).

Chapter 2 Component Software

Tools and platforms for deploying components and managing components at


runtime; this includes creation of component instances, finding and connecting
of instances, garbage collection, and shutdown or substitution of components;
platform standards may include the services that are available to all component instances.
Support of load balancing in distributed component systems.
Support for security policies and access rights management.
Tools and mechanisms for testing and analysis.
Naming and versioning support to uniquely identify components; mechanism
for defining and managing globally unique identifiers for component interface
definition.
Support to search for components with certain properties.
As can be seen from the list, component infrastructures can address many very
different aspects. In particular, an infrastructure includes technical aspects like
tools and libraries as well as standardization and policy aspects. Some of these
aspects will be considered as part of the description of component frameworks in
later chapters.
In our terminology, a component framework consists of a component model together with a coherent infrastructure. Component frameworks support developers
in building component software. In particular, they provide means that go beyond
classical programming languages and programming environments. Component
frameworks can be independent of the requirements of specific application domains. For example, OSGi can be used for the component-based realization of
software systems in almost all application domains (see Chapter 4). Component
frameworks can support general architectural patterns. For example, the EJB
framework primarily supports the construction of software systems according to
the three-tier architectural pattern (see Chapter 6). They can be developed for
more specific application areas (see the description of frameworks for embedded
application areas in Chapter 7). In that case, the component model and infrastructure are specialized to that area. Often predefined components are provided.
There is no sharp distinction between component frameworks and other kinds of
frameworks; in particular, frameworks can combine or use characteristics of different kinds of frameworks. Nevertheless, it is helpful to understand the characteristics. For this purpose, the following two questions provide a good guideline:
What is the component model?
What is the component infrastructure?
Let us look at two examples, the RMI mechanism and the graphical user interface
toolkit AWT, to illustrate aspects of component frameworks.

49

50

Chapter 2 Component Software

The mechanism for remote method invocation in Java, RMI, is a typical example
of a communication infrastructure. It provides library classes, interfaces, and services to connect to remote objects and invoke their methods. It can be used to
build up a component infrastructure, but it is not a component framework. Most
notably, a component model is missing.
The AWT is a typical program framework. It has an implicit, albeit very weak notion of components, namely the subclasses of the class Component. However, it is
missing a component infrastructure that would provide rules and techniques for
composition and deployment. The programmer is left with the raw class declarations to understand the interfaces of newly developed AWT components. That is
why Sun Microsystems developed JavaBeans (JavaBeans should not be confused
with Enterprise JavaBeans; both frameworks are very different and share almost
nothing except the name). The JavaBeans framework builds on the AWT by making the component model somewhat more explicit and by providing a light-weight
infrastructure. In particular, it defines rules and conventions for component interfaces and supports an event-based composition mechanism.

2.6

Problems for Chapter 2

Problem 2.1:
Differences
between
component models

Problem 2.1:

Problem 2.2:
Kinds of
composition

Problem 2.2:

Problem 2.3:
Component
declarations and
classes

Problem 2.3:

Problem 2.4:
Component
specifications

Problem 2.4:

Problem 2.5:
Multi-threading in
CBSD

Problem 2.5:

Name aspects in which component models can differ.

What kinds of composition are explained in the text? Describe the differences!

What are the commonalities of and the differences between component declarations and classes?

Why are component specifications important for separate development?

What are the problems caused by multi-threading in CBSD? Explain why several
attributes of the class Thermostat (Figure 2.13) are declared as volatile.

51

Chapter 2 Component Software

Problem 2.6:
How is synchronization between component activities established in the ACC
model?

Problem 2.7:
Can there be deadlocks in the CSC model? If the components in the CSC model
have to compute tasks of very different size and complexity, the central clock can
be a burden. Why?

Problem 2.6:
ACC model

Problem 2.7:
Deadlocks in the
CSC model

52

Chapter 2 Component Software

Chapter 3 Developing Component-Based Systems

Developing Component-Based Systems

Component-based software development is related and contributes to different


areas of software engineering. In general, it is a development discipline based on
three fundamental software engineering principles:
1. Divide and conquer: Divide a development task into subtasks and compose the
results of the subtasks.
2. Information hiding and encapsulation: Design clear interfaces between the
components of a system and specify contracts that describe how clients are allowed to use a component and what implementors have to guarantee.
3. Reuse: Develop software artifacts in such a way that they can be reused for the
realization of different systems.
Whereas there is not much dispute on this level of principles, the literature and
discussions become quite diverse when it comes to a more precise explanation of
what component-based software development is and how it is related to software
engineering. Here are some questions that are answered differently by different
people:
Is CBSD only about selecting and composing existing components? Or does it
include top-down engineering processes in which the divide step leads to the
development of new components?
Is CBSD only concerned with software components in the sense of Chapter 2,
that is, of software components including deployable implementations? Or
does it comprise the development of and with logical components (cf. Section
1.3)?
How is CBSD related to other development disciplines such as product-line
engineering, model-based development, and service-oriented development?
Approach. As there are no generally accepted answers to the above questions, we
could avoid the discussion of the relationship between CBSD and software engineering altogether. However, we think that it is important to discuss this relationship. In order to do so, we will give a somewhat narrow explanation of CBSD and
use this explanation as a basis for the discussion. We do not claim that our explanation is better than other explanations. Merely, we propose it for the sake of discussion. Using a somewhat narrow explanation is helpful to sharpen the boundaries to other engineering disciplines and aspects.
After the learning objectives and the explanation of CBSD, the chapter describes
the relationship to software engineering processes (3.3) and to software architecture and disciplines for reuse (3.4). Finally, Section 3.5 considers general aspects
of infrastructures and technical support. Specific aspects of infrastructure and

53

54

Chapter 3 Developing Component-Based Systems

technical support will be presented together with the description of component


frameworks in the following chapters.

3.1

Learning objectives for this chapter

After reading this chapter, you should


be able to explain what CBSD is and to discuss the normative aspects of this
explanation,
understand the relationship to software engineering processes and other development techniques,
have obtained a good idea of how architectural views relate to CBSD,
comprehend the role of componentization in general and CBSD in particular
as it relates to quality assurance,
have realized the importance of clear standards for tooling and infrastructure
support.
Furthermore, this chapter relates the material of this textbook to other courses that
focus on engineering aspects.

3.2

An explanation of CBSD

Whereas CBSD is often interpreted very broadly, we focus here on the core aspects and present a quite narrow, product-oriented notion. The reason for this approach is that, rather than selling CBSD, our main goal is to come up with crisp
criteria to discuss what CBSD is and what it isnt.
A component-based software system is a software system whose implementation is composed from software components that
have well-defined interfaces and a behavior that can be described independent of the contexts in which they are used;
have an implementation that can be composed and deployed independently;
follow a component model that determines the common computation, communication, and composition principles underlying a class of components. For
large systems, a number of different component models can be used.
Furthermore, we require that the component structure refines the (described) architecture of the system.
To check whether a given software system follows the explanation, one should
answer the following questions:

Chapter 3 Developing Component-Based Systems

1. What are the components of the system? The answer should be a finite list of
named components.
2. What are the interfaces of the components, what is their behavior? Ideally, the
resulting component specifications should not refer to specifications of other
components.
3. Which component model is used?
4. Does every component have an independently deployable implementation?
The answer could be a finite list of deployable units for each component.
5. What are the static and dynamic component structures of the system? Here,
static means that a component might be implemented with the help of other
components (cf. Figure 2.15 in Section 2.3.3). Dynamic means the possible
connections that component instances might have at runtime. It is important
that the structures are defined in terms of the component names given in step
(1.).
6. How is the composition described? Does it follow the component model?
7. Do the static and dynamic component structures of the system refine the described architecture? (More on this aspect can be found in Section 3.4.)
Our experience is that
before asking these questions, system architects tend to say that their software
system is component-based;
only very few systems satisfy all criteria.
Let us stress again that the idea of the explanation is not to say that a system is
badly designed if it does not follow all criteria. The criteria are meant to distinguish designs that are essentially component-based from those that take the notion
of software components less strictly or follow other disciplines. Based on the notion of component-based software systems, we can explain what CBSD is about:
Component-based software development is a sub-discipline of software engineering. Its goal is to provide concepts, methods, languages, techniques, frameworks, tools, standards, and an organizational infrastructure for the development,
provision, and selection of reusable software components as well as for the development of component-based software systems (in the above sense) using these
components.
Whereas our explanation is narrow with respect to software components and
component-based software systems, it is quite liberal with respect to the development processes for such systems. In particular, we do not restrict CBSD to search,
composition/assembly, and development of reusable components. As explained in
the following section, our explanation also covers top-down developments if they
result in a component-based software system.

55

56

Chapter 3 Developing Component-Based Systems

3.3

CBSD and software engineering processes

One central topic of software engineering is concerned with organizing the development or evolution of software systems into processes with well-understood
tasks and (intermediate) products. In this section, we discuss such engineering aspects of CBSD. In particular, we relate component-based development of new
software systems to traditional software development, sketch aspects of the evolution of component-based software and of the development of components, consider component selection and adaptation, and discuss quality assurance in the
context of CBSD.

3.3.1

Development of new systems

Software system development is based on four kinds of tasks: requirements analysis, design, implementation, and deployment. An implicit assumption underlying
the classical development process and more sophisticated variants of it is that the
implementation of the new software system is developed from scratch. Component-based software development starts with a different picture. The construction
of the new system should be accomplished by using existing software components. This goal shifts the focus of CBSD to two engineering problems:
1. Development with reuse: How can we bridge the gap between the requirements for a new system and existing components that can possibly be reused
to implement the system?
2. Development for reuse: How can we develop components such that they can
be reused? Here, reuse includes the task of finding components and checking
whether they satisfy the needs.
The first question addresses CBSD in top-down software development. The requirement to reuse components can be understood as a development constraint. To
solve the constraint, the software architecture has to be designed in such a way
that existing components can be plugged into the system. The second question
stresses bottom-up development where the constraint is reusability. Again, software architecture plays an important role, because reusability can rarely be achieved without suitable architectural information like a reference architecture, architectural patterns, or a component framework supporting architectural styles. As
software architecture and related issues are so important for CBSD, we treat them
in a separate section (Section 3.4).
Although CBSD has to address additional engineering aspects, most tasks of
CBSD can be accomplished using existing software engineering processes and
methods. Whether a certain process is appropriate depends on the task at hand.
Development scenarios with minor reuse can be treated as in traditional software
development (see, e.g., [17] for a software engineering approach that takes components into account). The main difference is that the resulting architecture and

Chapter 3 Developing Component-Based Systems

the components have to be in accordance with the explanations of Section 3.2. In


scenarios with a higher degree of reuse, it is helpful to use a method based on a
well-defined structure of software artifacts, such as the V-model (see [36]). This
allows organizing software development as a process of completing and refining a
partially filled artifact setting. Development of components for reuse can be organized like development of complete systems. The difference is not in the development process, but in the special requirements that enable reuse.
Some development aspects that are different from traditional software engineering
or that deserve a dedicated discussion are treated in the following subsections.

3.3.2

Evolution of software systems

Software evolution means modifying existing software systems in reaction to one


or several of the following requests:
Code maintenance: Typical reasons for maintenance are elimination of bugs,
improvement/refactoring of the components and their structure, and adaption
to new versions of the programming languages used.
Platform evolution: Typical reasons for platform evolution are new versions
of parts of the platform (e.g., for the operating systems, database management
systems, component frameworks, GUI frameworks, etc.).
Changing requirements: The change of requirements can refer to functional
or non-functional aspects (e.g., an additional feature or higher throughput in
parts of the system).
Because of its more explicit specifications and its close relationship between architectural components, implementation components, and deployment components, the evolution of component-based software is easier to manage. Code
maintenance profits from the strict structuring of component-based systems. In
particular, local changes can be verified directly against component specifications.
As all interfaces have to be explicitly described, it is clear from the specifications
which components are affected by platform evolutions. Often the software that
manages the component or the component containers in component frameworks
hides low-level platform evolutions. On the other hand, new versions of used
component frameworks can cause a substantial need for component evolution.
How helpful CBSD is for reacting to changing requirements depends on the kind
of changes. If components have to be added or only some components have to be
modified, the clear separation into components on all levels is usually profitable.
If the required change is of a cross-cutting nature (e.g., adding a certain security
policy to most of the components or a logging facility) or if the needed performance cannot be achieved with an implementation consisting of loosely coupled
components, a strict component-based software structure might be a burden. To

57

58

Chapter 3 Developing Component-Based Systems

solve cross-cutting changes, aspect-oriented techniques might be helpful (cf.


[27]). To solve performance problems, it may be necessary to give up independent
deployability of components; i.e., keeping the component structure on the design
level, but using more efficient and closer coupled communication mechanisms as
well as optimizations across component boundaries to achieve more efficiency.

3.3.3

Further development aspects

The development of components is similar to the development of small or medium-size software systems. Having a given, clear component model is helpful to
develop both the component specification and its implementation. Methods and
processes for component development are discussed, e.g., in [17] and [39] and are
not part of the scope of this textbook. What we like to stress here is the importance of CBSD for role separation and outsourcing of implementation tasks.
Role separation that is, structuring the engineering task into groups of developers and technicians with different skills, from different companies, and working
for different salaries is very important. CBSD supports this structuring in several ways. For outsourcing to other development teams, the specification together
with the component model and the component framework can be used as a clear
contract between the component vendor and the buyer. The buyer obtains a component that she can test against the specification. Independent deployability is very helpful for system integration. Furthermore, the strict structuring of CBSD, in
particular in connection with component frameworks, allows separating the development and maintenance of a system into five different roles: component provider/vendor, framework provider, application provider (constructs an application
from a number of components), application deployer (installs an application on a
platform), and system administrator.

3.3.4

Component selection and adaptation

When development for reuse is to be realized by third parties or organized as an


open component market, one is confronted with two challenges that play only a
minor role in classical software engineering:
1. Component selection: How can we find existing components that satisfy our
needs?
2. Component adaptation: How can we adapt a component to the context in
which it should be used?
Component selection consists of two sub-problems: retrieval and qualification.
Component retrieval means searching for possible component candidates that
might be suitable for the component requirements. As in information retrieval
tasks, a central aspect of component retrieval is the description of the properties of
the searched component. Components can be searched by name, by interface de-

Chapter 3 Developing Component-Based Systems

scriptions, or even based on behavioral properties (see, e.g., [42]). So far, there is
no widely accepted infrastructure for component retrieval.
The next step after having retrieved a number of candidates is component qualification. This is the process of determining which candidates are suitable for use
within the intended final system. Qualification includes
checking that the found components really have the properties used in the
search,
comparing the components in order to evaluate which component fits best,
testing and analyzing the chosen component(s) to make sure that their implementation satisfies their specification and further properties stated for the
component.
The last point is particularly important if the components come from third parties
or even untrusted sources. For test and analysis techniques, we refer to the subsection on quality assurance below.
In many cases, selected components cannot be used directly. They need adaptation before they can be integrated into the system or assembled with other components. Adaptation means modifying functional, non-functional, or technical
properties of a component such that the adapted component fits the needs of the
composition context. Adaptation techniques depend on the representation of the
component. In particular, we can distinguish:
White-box adaptation assumes that the implementation of the component is
available; it makes it possible to modify the program code of the component.
Gray-box adaptation is possible if we can wrap and extend the given component by some implementation parts without modifying the code of the component; the new wrapper together with the old component implementation
should form a software component, but the wrapper itself need not be a component.
Black-box adaptation takes the old component as given and implements a
new component that uses and possibly encapsulates the old component.
Some adaptations, in particular on the syntactical level and for technical properties, can be done automatically. Typical examples are renaming of interface types
and methods, integrating components written in different languages (e.g., embedding components written in C into a Java language context), and making components accessible from remote sites.

59

60

Chapter 3 Developing Component-Based Systems

3.3.5

Quality assurance

Quality assurance comprehends activities intended to ensure that the software


components and the composed systems satisfy their specification and requirements. These activities should be systematically integrated into the development
process. Most of the techniques for quality assurance developed for general software engineering can be applied to CBSD. In particular, testing and verification
techniques for quality control can be applied to CBSD (see, for example, [18] for
a book that focuses on quality control for component software). The differences in
practical application scenarios are that
specifications and component models are typically more detailed and precise
in a CBSD setting;
the integration problem is often harder in CBSD, because the component developers have no knowledge about the application context of the component;
the trust in third-party component suppliers might be lower than in tightly
coupled development teams; that is, the need for quality control is higher.
In summary, quality control benefits from the more explicit structures provided by
CBSD, but has to cope with the additional challenges resulting from separate development. The latter aspect is very visible in scenarios of mobile components
with proof-carrying code where safety guarantees are integrated as checkable
proofs into the deployable components (see [33]).

3.4

Software architecture as skeleton for reuse

The software architecture describes the overall system structures on an abstraction


level that links the behavior of the system to its intended realization as a software/hardware system. That is, software architecture can be understood as the
bridge between system requirements and implementation. For CBSD, software
architecture plays two roles:
1. In development with reuse, it provides the skeleton for integrating reusable
components.
2. In development for reuse, it provides the context for which reusable components are developed.
Before we discuss the relationship of software architecture and CBSD in general
terms, we like to illustrate that for practical use, application- or domain-specific
aspects might be at least as important. In particular, the degree of component coupling is an important factor. Here are three typical scenarios with a different degree of how components are coupled:
Loosely coupled applications: Software architecture is of minor importance
if the components are only loosely coupled in an open composition scenario

Chapter 3 Developing Component-Based Systems

(cf. Section 2.2 for open composition) and if the composition merely aggregates functionality, but is not designed to satisfy overall system requirements.
Typical examples are applications running on a PC or a mobile phone, coupled
(if at all) only by data exchanged over a file system or by other services of the
underlying platform. (Note, however, that in many professional scenarios,
PCs, laptops, or mobile phones are integrated into the workflows of larger systems.)
Protocol and data integrated systems: Software architecture is important for
systems that exchange data with system-specific formats and system-specific
protocols. Such systems have to guarantee system-wide invariants. Typical
examples are information systems such as the retail store example discussed in
Section 1.3. In such systems, it is relatively simple to integrate components
from different vendors, because the real-time constraints are low and they either run on general-purpose computers (like the database component of the
store management; see Figure 1.2 or they come with their own hardware (like
the scanner). The software architecture defines for a component provider how
its components can be integrated into the system.
Embedded systems: In many embedded system scenarios, the coupling constraints between components go beyond protocols and data formats. To
achieve integration, the software and system architecture has to cover realtime aspects and platform properties in much greater detail. For example, the
Java components for the thermo-control system considered in Section 2.3
might be worthless because the hardware for running a Java Virtual Machine
might be too expensive for a simple thermo-control device.
At least the last two scenarios should have illustrated that the integration of components into a system calls for more than just the specification of components. In
the next two subsections, we consider the relationship between CBSD and software architecture and between CBSD and other relevant development or architectural techniques.

3.4.1

CBSD and software architecture

Before we look into the relationship between CBSD and software architecture, we
will consider three prominent explanations of what software architecture is.
Then we will discuss the special architectural aspects of CBSD.
In Section 2.1 of [9], page 23, the following explanation of software architecture
is given:
The software architecture of a program or computing system is the structure or
structures of the system, which comprise software components, the externally visible properties of those components, and the relationships among them.

61

62

Chapter 3 Developing Component-Based Systems

The definition is given together with further explanation parts from which we
would like to cite as well:
"Externally visible" properties refers to those assumptions other components can
make of a component, such as its provided services, performance characteristics,
fault handling, shared resource usage, and so on. The intent of this definition is
that a software architecture must abstract away some information from the system
(otherwise there is no point looking at the architecture, we are simply viewing the
entire system) and yet provide enough information to be a basis for analysis, decision making, and hence risk reduction. Let us look at some of the implications of
this definition in more detail.
First, architecture defines components. The architecture embodies information
about how the components interact with each other. This means that architecture
specifically omits content information about components that does not pertain to
their interaction. Thus, an architecture is foremost an abstraction of a system that
suppresses details of components that do not affect how they use, are used by, relate to, or interact with other components. [...]
Second, the definition makes clear that systems can comprise more than one
structure, and that no one structure holds the irrefutable claim to being the architecture.
[...] By intention, the definition does not specify what architectural components
and relationships are. Is a software component an object? A process? A library?
A database? A commercial product? It can be any of these things and more.
Third, the definition implies that every software system has an architecture, because every system can be shown to be composed of components and relations
among them. [...]
Fourth, the behavior of each component is part of the architecture, insofar as that
behavior can be observed or discerned from the point of view of another component. This behavior is what allows components to interact with each other, which
is clearly part of the architecture. Hence, most of the box-and-line drawings that
are passed off as architectures are in fact not architectures at all. They are simply
box-and-line drawings. [...]
The introduction of [38], page 3, gives the following explanation for software architecture:
The architecture of a software system defines that system in terms of computational components and interactions among those components. Components are
such things as clients and servers, databases, filters, and layers in a hierarchical
system. Interactions among components at this level of design can be simple and
familiar, such as procedure call and shared variable access. But they can also be
complex and semantically rich, such as client-server protocols, databaseaccessing protocols, asynchronous event multicast, and piped streams.

Chapter 3 Developing Component-Based Systems

In addition to specifying the structure and topology of the system, the architecture
shows the correspondence between the system requirements and elements of the
constructed system, thereby providing some rationale for the design decisions. At
the architectural level, relevant system-level issues typically include properties
such as capacity, throughput, consistency, and component compatibility.
In [16], a book concentrating on patterns, the following definition can be found on
page 384:
A software architecture is a description of the subsystems and components of a
software system and the relationships between them. Subsystems and components
are typically specified in different views to show relevant functional and nonfunctional properties of a software system. The software architecture of a system
is an artifact. It is the result of the software design activity.
If we talk about the architecture of a software system, we refer to the different implicit and explicit structures underlying the system. Structures are expressed as
certain kinds of components not necessarily software components in the sense of
Chapter 2 and their connections or relations, i.e., the architecture captures system properties that are beyond algorithms and data structures. This is the focus of
the first definition and is a central part of most explanations of software architecture.
The second definition mentions another important and often neglected point. Architecture is about relevant system-level or system-wide issues and how they are
realized within the system. In addition to the properties named above, these include aspects like security, system availability, and fault tolerance. Thus, architecture is not only about the structuring of systems, but also captures the relationship
between the overall system behavior and the structured realization.
The third definition contains a remark about the nature of architectures: They are
artifacts, i.e., something explicit, the result of an activity. Notice that this idea is
contradictory to the third implication in the explanation of [9] saying that every
software system has an architecture, possibly an implicit one. However, it is an
important issue that architectures can be artifacts in their own right. They can be
designed and studied without going down to concrete realizations in the form of
software systems.
Architectural views. When it comes to developing a description of an architecture and to analyzing it, one has to select certain system structures and give their
components and relations a clear meaning. If an architecture is to be used as a
communication document between different stakeholders, it is important to find
some standards to select these structures. That is the goal of authors who propose
describing software architectures based on a fixed number of so-called views. An
architectural view concentrates on specific properties of the architecture or looks
at the architecture from a certain perspective. A view may combine several structures. In the literature, different views have been investigated. There is not yet a

63

64

Chapter 3 Developing Component-Based Systems

general consensus about what views should be used to describe an architecture.


Christine Hofmeister, Robert Nord, and Dilip Soni, for instance, center their approach to applied software architecture around four views (cf. [25]):
1. The conceptual view describes the system in terms of its major design elements and the relationships among them (often also called the logical view).
2. The module interconnection view encompasses two orthogonal structures:
functional decomposition and layers.
3. The execution view describes the dynamic structure of a system.
4. The code view describes how the source code, binaries, and libraries are organized in the development environment.
A similar approach is described by Philippe Kruchten in [28]. It is called the
4+1 view model of software architecture, i.e., it is based on four main views
plus a distinguished fifth view. Compared to the approach of Hofmeister, Nord,
and Sonis, the four views are tailored differently. The so-called physical view of
Kruchten, for example, takes into account the system's requirements such as system availability, reliability (fault tolerance), performance (throughput), and scalability. The physical view also describes how the various components identified in
the other three main views are mapped onto the processing nodes. In this respect,
it corresponds to the execution view above. The distinguished fifth view uses a
small subset of important scenarios (similar to UML use cases) to show how the
structures of the four views work together seamlessly.
For the purpose of this discussion, it is important that software architectures can
be expressed by different structures. The components of these structures can be:
virtual, i.e., they do not have direct counterparts in the deployed or running
system. For example, the components of the logical view, called logical components in Section 1.3, are used to explain the behavior of the overall system.
The code structures and execution structures may be different as long as they
realize the behavior.
static, i.e., they are related to concrete parts of the system's description and
implementation (computers, platforms, program modules, classes, procedures,
etc.).
dynamic, i.e., they correspond to runtime entities (OS processes, objects, procedure incarnations, execution state, established connections, events, etc.).
In particular, it should have become clear that components of architectural structures and views are not software components in the sense of this textbook.
Relating software architecture to CBSD. At the beginning of this section, we
argued that software architecture plays two important roles in CBSD, namely that
of

Chapter 3 Developing Component-Based Systems

a bridge between system requirements and implementation,


a skeleton for the integration of reusable components.
For a further, more detailed discussion, it is important to distinguish between the
different kinds of component composition (see Section 2.2). If component-based
systems result from open composition, it is the software architecture as an additional artifact (with corresponding implementation parts) that describes the composition and its properties. What is also specific for such systems from an architectural perspective is that on the level of software components, different architectural views refer to the same software components. In particular, the functional
decomposition of the system is based on the software components. The code
structure is clearly linked to the software components, as they are deployable
units. And the component instances, forming the central execution entities at runtime, are also tightly related to the software components used. Thus, a specific
property of component-based systems is that a software component appears in
several architectural views. This integrating property is important for a better understanding of the relationship between architectural structures and views.
In component-based systems resulting from hierarchical composition, the component paradigm even covers more architectural aspects. Hierarchical componentbased systems are structured in such a way that their components are subsystems
following the same principles as complete systems. At least in principle, the structures of hierarchical component-based systems provide all the information of the
module interconnection view, the execution view, and the code view. Functional
partitioning corresponds to components on the same hierarchical layer; layered
decomposition corresponds to the component hierarchy (module interconnection
view). The connection between components at runtime is handled by the enclosing component; the interface behavior of components is part of their specification
(execution view). The relationship of the components to their code is worked out;
more precisely, the component developer has to precisely define the code base of
every component.
For heterogeneous composition, the situation is somewhat in the middle between
open and hierarchical composition disciplines. In all three cases, software components can be seen as entities combining different views: They are linked to the
software architecture by their specification, integrate functional behavior with corresponding implementations, combine program modules into deployable units,
and allow instantiation of dynamic objects at runtime.

3.4.2

CBSD and other development techniques

CBSD is not the only development technique that aims to improve reuse and
software evolution. In this section, we sketch the relationship of CBSD to:
Service-oriented development and service-oriented architecture (SOA)

65

66

Chapter 3 Developing Component-Based Systems

Aspect-oriented software development (AOSD)


Model-driven development and model-driven architecture (MDA)
Product-line engineering
Whereas SOA is currently mainly used for information systems and AOSD is independent of the application area, model-driven development and product-line
engineering are widely used for embedded systems. The goal of the discussion
here is to sharpen the understanding of CBSD and to show how the techniques
complement one another.
Service-oriented development. The focus of service-oriented development is to
provide services to clients. From the view point of a client, a service realizes a
non-technical task, such as placing an online booking or airline ticket order. Services can be orchestrated into more complex services. Service orientation emphasizes loose coupling of services, a meta-data infrastructure for specifying service
properties, independence of the service client from the service provider, and dynamic composition of services into workflows. A service-oriented architecture
should allow selecting a service by its properties and not by the name of its provider.
CBSD and service-oriented development have in common that they provide functionality as services that are specified in an implementation-independent way.
CBSD is usually considered to be independent of the application area. Services in
CBSD might have fine granularity and depend on other services in a complex
way. In contrast, services in service-oriented development are typically used for
communication in information systems with a coarser service granularity. Serviceoriented architecture focuses on techniques for service discovery and service orchestration. CBSD focuses on implementing encapsulated functionality through
deployable code units. CBSD can be used as a realization technique for SOA.
SOA can be used as a flexible orchestration layer on top of certain component
systems providing services of coarser granularity.
Aspect-oriented software development. Aspect-oriented software development
(AOSD) in general and aspect-oriented programming (AOP) in particular are
techniques that support modularity with respect to so-called cross-cutting concerns. Cross-cutting concerns are aspects of a system's functionality that cannot
be realized within one component, but are spread over large parts of the system.
Typical cross-cutting concerns are logging functionality (affecting every single
logged part of the system), checking and monitoring of interfaces, system-wide
error handling, and adaptation aspects. In a classical module design, the functionality of cross-cutting concerns would be part of most modules of the system, making the treatment of these concerns in design, implementation, and maintenance
quite hard. AOSD and AOP allow describing and implementing cross-cutting concerns in one place and support techniques for weaving the functionality into the
executable code.

Chapter 3 Developing Component-Based Systems

AOSD and AOP can be considered as being orthogonal to CBSD. Whereas CBSD
stresses strict partitioning of the system's functionality into deployable components, AOSD supports the treatment of system-wide aspects as layers that can be
automatically added to software components. That is, CBSD and AOSD are complementary techniques that should be used together.
Model-driven development. Model-driven development structures the development process of software systems and single components into refinement steps. It
starts with a model that captures the central system behavior in an abstract way,
focusing on what happens and not on how it is computed (CIM). The next model
level describes how such systems can be realized in a platform-independent way
(PIM). By refining this model with respect to particular platforms, a platformspecific model (PSM) is constructed. This model is used to generate and program
the needed system-specific code. Model-driven development is very helpful if
systems or components are to be implemented for very different platforms and if
the application area allows automating model transformations or generating a substantial part of the code.
Whereas CBSD emphasizes the structuring of systems into software components,
model-driven development focuses on structuring the development process into
several refinement steps. Thus, both development techniques are orthogonal and
can be combined (see [39] for model-driven development of components). The
terminology of model-driven development allows us to explain more precisely
what we meant by ..., we require that the component structure refines the (described) architecture of the system in the explanation of component-based software systems in Section 3.2. In CBSD, we expect that the component boundaries
determined and designed on the CIM-level remain the same throughout the refinement process down to the deployable code.
Product-line engineering. Product-line engineering develops architectural structures and software artifacts for families of software products within a company or
organization. The idea is that similar products can be developed and maintained
more cost-effectively if they share a common design and common parts of the implementation. The development of a product line includes in particular:
Scoping, which determines the products covered by the product line
Feature modeling, which describes the set of features available in products
A product-line architecture, which explains how the artifacts of a product
line are related and can be composed
Implementation artifacts and components, which allow deriving single
products of the product line.
Product-line engineering is an approach for the planned and effective reuse of
software artifacts and components. Rather than developing software components
for general use, product-line engineering creates software artifacts when reuse can

67

68

Chapter 3 Developing Component-Based Systems

be predicted. Product-line engineering uses component-based software technology, in particular for product derivation, but exploits other forms of generic and
composable software artifacts as well.

3.5

Language and tool support

As explained in the sections above, CBSD is a development technique that spans


design, implementation, and deployment. It can be used in classical development
settings in which designs and architectures are described informally or by some
modeling language, in which implementations are realized in some high-level
programming language, and in which configuration and deployment is done
manually or with the help of platform-specific tools. The goal, however, is to
equip CBSD settings with integrated language and tool support for component
specification, implementation, packaging, versioning, configuration, and deployment. In addition, support for component selection and automated adaptation is
desirable. In most of these areas, technology development has made substantial
progress in the last decade. In particular, new component frameworks have been
developed and existing ones have been improved.
In many application areas, component frameworks are nowadays an integral part
of system development. They are supported by integrated software development
environments and provide many important services and facilities, like support for
persistence, security, version management, transactions, etc. The following chapters will explain three central goals of component frameworks together with major
component frameworks focusing on these goals:
Packaging and dynamic management of components (with OSGi)
Language independency and distribution (with CORBA)
Support for more application-specific aspects in component frameworks (with
EJB)
In the last chapters, we will consider other technical support for CBSD, in particular for embedded system scenarios.

3.6
Problem 3.1:
Design with reuse
and design for
reuse

Problems for Chapter 3

Problem 3.1:
CBSD is about design with reuse and design for reuse. Explain these terms and
relate them to top-down and bottom-up software development methods. What is
the role of architecture in the two development scenarios?

69

Chapter 3 Developing Component-Based Systems

Problem 3.2:
Name the three main reasons for software evolution and why CBSD can help to
perform evolution steps. Name situations in which CBSD cannot be of help for
evolution.

Problem 3.3:

Problem 3.2:
Software evolution

Problem 3.3:
Role separation

Explain role separation and name five different roles. Why is role separation important for software development?

Problem 3.4:
Explain the three adaptation techniques mentioned in the text and how they differ.

Problem 3.5:
What are the differences between product-line engineering and CBSD?

Problem 3.4:
Adaption
techniques

Problem 3.5:
Product-line
engineering and
CBSD

70

Chapter 3 Developing Component-Based Systems

Chapter 4 General Component Framework Concepts

General Component Framework Concepts

Component frameworks support the management of software components and the


composition of software systems from such components. As introduced in Section
2.5, a component framework
defines a component model and
provides an infrastructure for the components according to this model.
As explained in Section 2.4, the component model determines the common computation, communication, and composition principles and rules underlying a class
of components. Having such common principles and rules allows developing
components more independently, for example, by separate development teams or
different companies. Component frameworks are particularly important for integrating components from different parties and creating markets for prefabricated
software by setting rules and standards that span institution boundaries (cf. Section 1.2).
After the learning objectives, this chapter explains the central concepts of component frameworks (Section 4.2) and describes how they are realized within the existing framework OSGi (Section 4.3). OSGi extends the Java platform with the
notion of deployable components. It can be used for any kind of system. The goal
of the chapter is to focus, substantiate, and refine the general concepts of component software introduced in Chapter 2 and to provide the basic knowledge for developing and using OSGi components. Finally, we explain and discuss further
framework features and other approaches to component frameworks (Section 4.4).
The reader is advised to reread Section 4.2 after having studied Sections 4.3 and
4.4.

4.1

Learning objectives for this chapter

After studying this chapter, the reader should


have a clear understanding of how the general concepts and goals of component software are supported by component frameworks,
be able to develop and deploy simple components with OSGi and use the basic
features of these frameworks,
understand the strengths and weaknesses of the framework,
know about other approaches.

71

72

Chapter 4 General Component Framework Concepts

4.2

Component frameworks: Notions and core concepts

As introduced in Section 2.5, components are the units of composition in the context of a component framework. The goal of component frameworks is black-box
reuse. That is, components are reused based on their specified interfaces and their
explicit context dependencies only where context dependencies capture the requirements made on the environment in which a component executes. Inspection
of the component's implementation by the user should be unnecessary and is often
not possible.
A component framework CF supports the definition of components as well as
their deployment, connection, and use. In particular, a component framework has
to answer the following questions for component developers and users:
How are components named and identified?
What descriptions are needed to develop a component?
How are components deployed?
How can components be found and used by other components?
What platform and runtime support is needed to work with a particular component framework?
Before explaining in the following sections how these questions are answered by
the existing framework OSGi, we will discuss them on a conceptual level. There
are two reasons for this slightly more ambitious approach:
We want to introduce terms for central concepts that are independent of existing frameworks/technologies. This is helpful because the same concept is often named differently in different frameworks. Working with frameworkindependent terms helps to compare frameworks.
We want to first focus on what has to be provided by a component framework
and focus on how it is realized only in a second step.
We first describe the conceptual implementation layers of component systems.
Then, we explain the typical concepts underlying naming and identification of
components and component implementation. Subsequently, we consider deployment and composition. Finally, we present a self-made ultra-lightweight component framework, called TCF (for Tiny Component Framework), which we developed as a first illustration of the described concepts.

4.2.1

Layers: platforms, containers, and components

In Section 2.2, we said that software systems consist of two layers, a platform and
the system of interest (see Figure 2.1). In CBSD with component frameworks, we
can distinguish three conceptual implementation layers (see Figure 4.1):

Chapter 4 General Component Framework Concepts

73

Figure 4.1:
A platform with
two containers and
deployed
components

Platform: If we say that a component-based system runs on a platform, the


term platform refers to the used computer/s, the operating systems, and
possibly further layers of infrastructure and virtualization, most notably network and general middleware support and virtual machines (like, for example,
the Java Virtual Machine). The platform comprises those parts of a running
component-based system that are independent of and do not belong to the
component framework.
Container: The container of a component framework provides the functionality for managing components. The container runs on the platform and is
the location of deployment, i.e., a component C is deployed in a container D.
In that case, we also say that D contains C. The container handles registration
and connection of components. Furthermore, it makes general services available. The container is part of the component infrastructure and independent of
the developed component-based system.
What we and others call the container is sometimes identified as the framework; sometimes it is called the platform. The container and its properties are
specified by the rules of the component framework.1
Components: Components embody the specific functionality for developed
component-based systems. Their concepts are considered below.
Similar to the instance relation between classes and objects, we can distinguish on
each of the layers the type or description level on the one hand and the instance

We prefer the term container over alternative names, because the notion of containers is often used and
easily extends to settings with hierarchical deployment where a container is deployed as a component
within another container.

74

Chapter 4 General Component Framework Concepts

level on the other hand. For example, we can talk about JVM Version 6.0 running
on Linux as a type of a platform. We can also talk about a laptop as a specific instance of such a platform. The same is true for containers and components. At
execution time, there might be several container instances running on a given platform instance. Each container instance might contain several components and
their instances.

4.2.2

Components: identifiers, bodies and specifications

From a conceptual point of view, a software component C consists of three items:


a component identifier id, a component specification spc, and a component body
bdy; we write C = <id,spc,bdy>. In the following, we explain these items and
their relations.
In many component frameworks, the identifier id consists of a name n and a version number v; we write id = n:v. The name is unique in a given name space.
Typical name spaces in the context of component software are a container, a platform, an organization, or a global name space managed by some community. In a
name space, we can ask whether there is a component with the name n and, if so,
which component is bound to n. That is, for each point in time, at most one specification and one body is bound to the name n in a given name space. In another
name space, n might name a different component. Version numbers are used to
support the evolution of components over time. For example, a user of a component with the name n may ask for a new version of this component (in a given
name space). We assume that names and version numbers can be compared regarding equality by looking at their textual representations.
Component specifications express the information that is needed to search for
and use components. In particular, specifications capture the names and types of
the services offered. Depending on the considered component frameworks, specifications may be informal or formal; they may be complete or specify only some
properties. The typical situation is that the types and names of the component's
interfaces are specified in a formal syntax, whereas the behavior is kept informal
and incomplete. In many cases, component specifications are only given implicitly. We assume that we can compare specifications regarding equality, although it
is sometimes difficult in practice to perform this check. In particular, we say that
two components C1 = <id1,spc1,bdy1> and C2 = <id2,spc2,bdy2> implement
the same specification if spc1 = spc2. A typical query is to ask whether there is a
component in a container that implements a specification spc.
The component body consists of the descriptions, the code, and the other resources needed to deploy a component. We assume that bodies can be compared
regarding equality by looking at their text or byte representations. As indicated
above, the component body comprises the code for the component's implementation. However, just like a component can use another component's services, a

Chapter 4 General Component Framework Concepts

component can share code with other components. We will explain such mechanisms together with the deployment and composition concepts in the next subsection.
We assume that component frameworks support the notion of well-formedness of
components <n:v,spc,bdy>. This includes well-formedness of the name n, the
version number v, the specification spc, and the body bdy. In a well-formed component, bdy implements spc. If not stated otherwise, we will consider only wellformed components in the following.

4.2.3

Composition mechanisms: bundling, deploying and


connecting components

A component framework provides mechanisms for putting components together


and supports the communication infrastructure for connecting components. The
composition mechanisms explain how components are composed and deployed
in a framework. They describe the rules on how a component finds other components by name (e.g., based on some naming service), how components can import
code from other components, and how components can provide functionality to
and make use of the functionality of other components.
Furthermore, component frameworks specify interfaces that allow inspecting
components at runtime (e.g., to check which services they provide). They may
support mechanisms for starting up and initializing remote components. They may
comprise library code that components need to connect to other components. They
may define standardized platform components for composed systems or specify
tools for composition and deployment.
Depending on the composition time and the goal of the composition, we distinguish the following composition mechanisms:
Bundling means constructing a new component C from the given components
C1, ... , Cn before deployment. C is a component with its own name, specification, and body. Sometimes bundling is called aggregation. A typical usage of
bundling is to develop an application component from other more basic components. Bundling is a purely static composition mechanism.
Deploying means making the name and body of the components C available
within a container D. Names and code used by C are resolved with respect to
the current name space of D. Deployment happens before any use of C. Depending on the component framework, this may happen
statically, that is, before the deployed components are started in that
case, this corresponds to the installation of system components on a platform;

75

76

Chapter 4 General Component Framework Concepts

dynamically, that is, when C is plugged into a running system in that


case, this corresponds to dynamic loading of code, classes, or libraries.
In both cases, we consider the classical task of linking code, that is, the resolution
of symbolic names as part of the deployment.
Connecting means making the services of one component available to another
component at runtime. This is usually done by passing references from one
component to another. The connection mechanisms often support the communication between components of different processes (remote procedure call or
remote method invocation).
Of course, the different composition mechanisms can be used together in a
framework. In some component frameworks, it is even transparent to the user
which composition mechanism is applied.

4.2.4

Illustrating component framework concepts

Real-life component frameworks are complex entities. For beginners, it is often


difficult to understand the basic structures and design decisions underlying component frameworks. This is in part because of the complex requirements they have
to fulfill, in part because of compatibility constraints. Thus, to simplify the task of
illustrating the concepts described above and explaining the core ideas and problems of component frameworks, we develop an ultra-light-weight component
framework of our own. It is called tiny component framework, or TCF for short.
TCF can be understood as a miniature core of OSGi and is a first step towards
learning OSGi.
TCF is a homogeneous component framework for components written in Java.
Components follow the MTC model and provide and require so-called services.
The loading and startup of components is managed by the TCF container according to a configuration file. In the following, we describe TCF, explain its use, illustrate the introduced component framework concepts and typical conventions,
and discuss some of TCF's shortcomings.
Components. A TCF component has a name that has to be different from the
names of all other components that should be used together with it. Components
provide and require so-called services. Services are identified by a Java interface
type.
As an example, we consider a component called zipCode, which provides a service Address2Zipcode. The service takes an address string and returns the corresponding zip code:
package services;
import tcf.framework.TCFService;
public interface Address2Zipcode extends TCFService {
String getZipcode(String address);

Chapter 4 General Component Framework Concepts

}
In order to be registrable within a TCF container, a service in TCF has to extend
the type TCFService defined in the package tcf.framework.
A component specification is given by the interfaces and behavior descriptions
of the services it provides and the interfaces of the services it requires. A component body consists of a so-called component header and the implementation of
the provided services; that is, for each service, there has to be a class implementing the service interface. TCF requires that the body is realized within a number
of Java packages, with one of them having the same name as the component. The
header class has to implement the interface TCFComponent of the package
tcf.framework:
public interface TCFComponent {
void start(TCFContext tcx) throws TCFException;
}
The interface TCFContext is another interface defined in tcf.framework. It enables
access to the context of the component in the container. In TCF, the context is
only used to register and lookup services:
public interface TCFContext {
void registerService(String name, TCFService svc);
TCFService getService(String name);
}
The header class creates the objects implementing the services of the component
and uses the context provided by the TCF framework to register services. Assuming that the class Addr2ZipImpl implements the service Address2Zipcode, a
header class for a component zipCode implementing the service could be the following:
package zipCode;
import tcf.framework.*;
public class TCFComponentHeader
implements TCFComponent
{
public void start(TCFContext tcx) {
tcx.registerService("Address2Zipcode",
new Addr2ZipImpl());
}
}

77

78

Chapter 4 General Component Framework Concepts

Figure 4.2:
The components of
the phone number
example

As a comprehensive example with multiple components, we consider the scenario


depicted in Figure 4.2: The component phoneNumberNA provides the service
NameAddress2PhoneNumber, which takes a name and an address and returns the
corresponding phone number:
public interface NameAddress2PhoneNumber
extends TCFService {
String getNumber(String name, String address);
}
The implementation uses the service Address2Zipcode from above and a service
NameZipcode2PhoneNumber, which yields the phone number for a (name, zip
code)-pair:
public interface NameZipcode2PhoneNumber
extends TCFService {
String getNumber(String name, String zipcode);
}
Figure 4.3 shows the implementation of the component header for phoneNumberNA and the implementation of its service. The header connects to the required
services and registers its provided service. It is important to note that the component phoneNumberNA does not have to know the names of the components that
provide the required services. Only the names of the required services are used in
the implementation. In particular, we can use different components providing the
required services without having to modify the component phoneNumberNA.

Chapter 4 General Component Framework Concepts

79

package phoneNumberNA;
import tcf.framework.*;
import services.NameZipcode2PhoneNumber;
import services.Address2Zipcode;
public class TCFComponentHeader implements TCFComponent
{
public void start(TCFContext tcx) throws TCFException
{
Address2Zipcode a2z;
NameZipcode2PhoneNumber nz2p;
a2z = (Address2Zipcode)
tcx.getService("Address2Zipcode");
nz2p = (NameZipcode2PhoneNumber)
tcx.getService("NameZipcode2PhoneNumber");
if( nz2p == null || a2z == null ) {
throw new TCFException("phoneNumberNA:
services not available.");
}
tcx.registerService("NameAddress2PhoneNumber",
new NameAddr2PhoneImpl(a2z,nz2p));
}
}
class NameAddr2PhoneImpl implements NameAddress2PhoneNumber
{
Address2Zipcode a2z;
NameZipcode2PhoneNumber nz2p;
NameAddr2PhoneImpl( Address2Zipcode a2z,
NameZipcode2PhoneNumber nz2p ){
this.a2z = a2z;
this.nz2p = nz2p;
}
public String getNumber(String name, String addr) {
return nz2p.getNumber(name, a2z.getZipcode(addr) );
}
}

To start a system composed from several components, one component has to implement and register the special service TCFMain of the package tcf.framework:
public interface TCFMain extends TCFService {
void run();
}
The method run is called by the TCF container after all components are loaded
and started. In the phone number scenario of Figure 4.2, the component phoneNumberGUI implements the service TCFMain, that is, the run- method by a
class GUIPhoneNumperImpl (not shown). This class provides a simple graphical

Figure 4.3:
phoneNumberImpl

80

Chapter 4 General Component Framework Concepts

user interface for using the services NameZipcode2PhoneNumber and NameAddress2PhoneNumber (see Figure 4.4 for the component header and Figure 4.5 for
a possible layout of the GUI).
public class TCFComponentHeader implements TCFComponent {
NameZipcode2PhoneNumber nz2p;
NameAddress2PhoneNumber na2p;
public void start(TCFContext tcx) throws TCFException {
nz2p = (NameZipcode2PhoneNumber)
tcx.getService("NameZipcode2PhoneNumber");
na2p = (NameAddress2PhoneNumber)
tcx.getService("NameAddress2PhoneNumber");
if( nz2p == null || na2p == null ) {
throw new TCFException(
"phoneNumberGUI: services not available.");
}
tcx.registerService("TCFMain",
new GUIPhoneNumberImpl(this) );
}

Figure 4.4:
phoneNumberGUI

Figure 4.5:
A graphical user
interface for the
phone number
application

To illustrate typical rules regarding standards and formats of component frameworks, we summarize the rules that TCF components have to satisfy: A TCF component is implemented by one or more packages, one of which has the same name
as the component. The specification is given by the interfaces and descriptions of
the provided services and the interfaces of the required services. The interfaces of
the services are contained in a special package named services. The package of
the component implementation contains the header class and all dedicated implementation parts. It can import from the other packages of the component, from
tcf.framework, and from the packages of the standard Java library.
Deployment. In TCF, deployment of a component-based system consists of the
following steps, where <deployment-dir> stands for the directory from which the
TCF container loads components (see below):

Chapter 4 General Component Framework Concepts

1. Compile the components and create a jar-archive for each component. If


<comp_name> is the name of the component, the corresponding jar-archive
should be named <comp_name>.jar. It has to contain all packages of the component's implementation.
2. Copy the jar-archives to <deployment-dir>.
3. Copy the service interfaces to <deployment-dir>.services.
4. Create a configuration file for the system. The file has to contain the names of
the components in the order in which the components should be loaded and
started. Each line contains one component name.
One component in the system configuration has to implement and register the service TCFMain (see above). For the above example, the configuration file looks as
follows, where component phoneNumberGUI provides the service TCFMain:
phoneNumber
zipCode
phoneNumberNA
phoneNumberGUI
Every component framework has its own deployment rules and configuration
mechanisms. Important to note here is that the rules and mechanisms are defined
by the framework and are independent of the component-based system to be deployed. Thus, a component can be deployed for many systems without the need to
adapt to different deployment rules as long as the same component framework is
used.
Platform and container. TCF runs on all platforms supporting a command line
interpreter, a file system, and a Java Virtual Machine (JVM), in particular on all
Windows, Linux, and MacOS PCs. It comes with a container implementation for
loading and starting components. The container also manages the registration and
lookup of components (i.e., it provides an implementation for TCFContext). To
start the container, the JVM is started with the command:
java -cp <CLASSPATH>
tcf.container.TCFContainer <configuration-file>
where <CLASSPATH> has to contain the deployment directory, all jar-archives,
and the directory of the package tcf. The container loads and starts the components given in the configuration file in the order they are listed. Component startup in particular establishes the connections between the components by registering and looking up the provided and required services. Finally, the container calls
the method run of the service TCFMain to start the component-based system.

81

82

Chapter 4 General Component Framework Concepts

(The implementation of the container can be obtained from the author of this
course.)
package phoneNumberBundled;
import tcf.framework.*;
import services.*;
public class TCFComponentHeader implements TCFComponent {
Address2Zipcode a2z;
NameZipcode2PhoneNumber nz2p;

Figure 4.6:
phoneNumberBund
led

public void start(TCFContext ctx) throws TCFException {


new phoneNumber.TCFComponentHeader().start(ctx);
new zipCode.TCFComponentHeader().start(ctx);
new phoneNumberNA.TCFComponentHeader().start(ctx);
}
}

Bundling. Bundling means constructing a new component from given components so that only the new component is visible during deployment. As an example, we bundle the components phoneNumber, zipCode, and phoneNumberNA
into the new component phoneNumberBundled. To do so in TCF, we create a new
package phoneNumberBundled with a new component header as shown in Figure
4.6. The new header starts the bundles components. The jar-file of this component
has to contain all four packages and is used during deployment like any other
component. In particular, the configuration file needs only one entry for this component. Thus, for the user of the component, it is not visible that it is a bundle of
several components.
Discussion. TCF is not a realistic component framework. It was designed to illustrate core features of component frameworks in a simple way. The reader should,
in particular, have understood and gained some intuition on the following aspects:
Syntactic rules that components have to follow
Bundling (or aggregation) of components
Deployment and configuration of systems from components
Loading and startup
Connection of component instances at runtime
Loose component coupling: A component using a service of another component is independent of the component that provides the service. In TCF, the
component providing the service is determined by the configuration, that is,
much later than the component development time.

Chapter 4 General Component Framework Concepts

Realistic component frameworks are more general in all or most of the above aspects. Many of the generalizations will be discussed together with the component
frameworks treated in the rest of the book. In addition, we will consider aspects
that are not illustrated by TCF, such as searching and dynamic selection of components, component distribution and load balancing, mechanisms for garbage collection, versioning, and language independency. Another important aspect is tool
support. Whereas we have presented basic realization techniques here, it should
be noted that the standardization underlying component frameworks simplifies
tool support during all stages of development.

4.3

OSGi: A component framework for Java

OSGi is a component framework for Java. In particular, it supports dynamic installation and management of components. Complete applications and single
components can be remotely installed, started, stopped, updated, and uninstalled
without requiring a reboot of the host process. OSGi extends the mechanisms for
managing Java packages and classes by introducing so-called bundles as deployable units. Furthermore, it supports a life cycle model via APIs and management
policies. Bundles are registered with a service registry so that other bundles can
observe the addition of new services, or the removal of services, and adapt accordingly.
OSGi originally stood for Open Service Gateway initiative. Currently, it is marketed under the header OSGi The dynamic module system for Java (see
http://www.osgi.org). The OSGi framework is supported by a non-profit industry
alliance including a large number of top international software companies. The
alliance provides specifications, reference implementations, test suites, and certification to foster a cross-industry market and container for components. Alliance
members represent diverse markets, including SmartHome, automotive electronics, mobile and enterprise. Member company industries include leading service
and content providers, infrastructure/network operators, utilities, software developers, gateway suppliers, consumer electronics/device suppliers (wired and wireless), and research institutions. Corresponding to the alliance members, OSGi is
used in a number of different areas, including automobiles, mobile devices, and
home automation.
The OSGi framework specification has many implementations, including open
source implementations like Apache Felix (http://felix.apache.org) and Eclipse
Equinox (http://www.eclipse.org/equinox/). The examples of this chapter are developed with Eclipse Equinox, but should run with other implementations as
well. When this text was written, the current OSGi version was Release 4, Version
4.1. Even the core specification of this version has 288 pages. Thus, it is beyond
the scope of this book to explain the details of OSGi. Rather, this section has the
following goals:

83

84

Chapter 4 General Component Framework Concepts

It provides an introduction to OSGi that should enable the reader to develop,


deploy, and use simple component systems with OSGi.
It uses OSGi to further illustrate the concepts explained in Section 4.2 and to
cover additional features of component frameworks.
We start with an introduction to the very basic concepts of OSGi along with a tiny
example. Then, we use a more realistic example to further explain aspects of how
component code can be organized and how dynamic reconfiguration can be handled. The last subsection contains a short overview of further features.

4.3.1

Basic concepts of OSGi

We start the description of OSGi with aspects that are similar to TCF:
OSGi defines the structure and syntax of deployable components, so-called
bundles.
It provides a container together with a sophisticated API to install and dynamically manage components. Both together are called the OSGi framework. The framework interfaces and classes are contained in the package
org.osgi.framework .
To illustrate the explanations, we use an almost trivial service, which provides a
random number on request, and a client using this service.
Bundles. In OSGi, the units of modularization and deployment are called bundles.
Bundles can import and export packages. Similar to the TCF header class, they
can include a so-called activator class for startup. Syntactically, a bundle is
comprised of
a manifest file describing the contents of the bundle and providing information
for the installation and management of bundles,
Java interfaces and classes organized into packages,
optional data and resource files such as pictures and HTML files,
optional documentations, texts, and sources.
Bundles are stored and deployed as jar-archives. For example, the bundle of the
random number service is a jar-archive containing the following documents:
the manifest file shown in Figure 4.5,
the interface of the provided service given in Figure 4.6,
the class implementing the service given in Figure 4.7,
the activator class given in Figure 4.8.

Chapter 4 General Component Framework Concepts

85

We will discuss these documents in turn.


Bundle-ManifestVersion: 2
Bundle-SymbolicName: randomNumberService
Bundle-Activator: randomnumberservice.impl.Activator
Import-Package: org.osgi.framework;version="1.3.0"
Export-Package: randomnumberservice

Figure 4.5:
randomNumberMa
nifest

The manifest carries descriptive information about the bundle. As demonstrated


by the example in Figure 4.5, it consists of a list of (header, value)-pairs. The
OSGi specification defines over twenty headers and describes which are mandatory and which are optional. The headers shown in the example have the following meanings:
The Bundle-ManifestVersion header defines which release of the OSGi specification the bundle follows. The number 2 refers to Release 4.
The Bundle-SymbolicName specifies a unique name for the bundle. If necessary, a more readable name can be defined by the header Bundle-Name.
The name of the activator class is given after the header Bundle-Activator.
Package imports and exports are declared following the headers ImportPackage and Export-Package. If several packages are imported or exported,
they are separated by commas.
A bundle has to declare the packages it imports and can declare packages for export. It can specify the version of the package. The general mechanism for import
and export will be described in Subsection 4.3.2. Here, we briefly discuss the imports and exports of the example. The bundle has to list the OSGi framework package as import, because the activator class needs access to the container API (see
Figure 4.8). Packages from the Java standard library need not be listed. The bundle exports the package randomnumberservice. It should be noted that this package only contains the service interface. The implementation of the service and the
activator class are not exported, as they belong to the non-exported package randomnumberservice.impl.
package randomnumberservice;
public interface RandomNumberService {
// Returns a pseudorandom int number.
public int getRandomNumber();
}
package randomnumberservice.impl;

Figure 4.6:
randomNumberSer
vice

86

Chapter 4 General Component Framework Concepts

import randomnumberservice.RandomNumberService;
import java.util.Random;

Figure 4.7:
randomNumberSer
viceImpl

public class RandomNumberServiceImpl


implements RandomNumberService {
Random rand;
/**
* Initializes the random number generator. The method
* must be called by the bundle registering the service.
*/
void initService() { rand = new Random(); }
public int getRandomNumber(){ return rand.nextInt(); }
}

The service interface and its implementation in the Figures 4.6 and 4.7 should
need no explanations. Note that the initialization method is not available outside
the bundle and that the package java.util.Random can be imported as is from the
Java standard library.
package randomnumberservice.impl;
import java.util.Properties;
import org.osgi.framework.*;
import randomnumberservice.RandomNumberService;
public class Activator implements BundleActivator {
private ServiceRegistration reg = null;
public void start(BundleContext bctxt) throws Exception
{
System.out.println("randomNumberService starting");
// Create service object and initialize it.
RandomNumberServiceImpl rnsi =
new RandomNumberServiceImpl();
rnsi.initService();
System.out.println("randomNumberService initialized");
// register service
reg = bctxt.registerService(
RandomNumberService.class.getName(),
rnsi, new Properties());
System.out.println("randomNumberService registered");
}

Chapter 4 General Component Framework Concepts

public void stop(BundleContext bctxt) throws Exception


{
System.out.println("randomNumberService stopping");
if (reg != null) reg.unregister();
}
}

The task of the activator class in OSGi corresponds to the task of the header class
in TCF. The activator has to implement a start method that
creates and initializes the component instance or instances,
looks up service references and service objects of required services,
registers service listener objects for required services if necessary, and
registers the services provided by the bundle.
And it has to implement a stop method that performs the corresponding steps for
de-registration when a bundles or service is modified or shutdown (see Section
4.3.3. for details). Start and stop methods receive the bundle context as parameter,
which enables access to the API of the framework. The activator class of the random number services (Figure 4.8) shows how to register and unregister a service
using the method registerService of the type BundleContext. Registration needs
three parameter:
1. The services type name (RandomNumberService.class.getName()).
2. The reference to the object implementing the service.
3. An object describing properties of the service (in the example, no properties
are described).
Registration returns a registration object that should be kept private by the component instance. It can be used to modify the service's properties and to unregister
the service (see method stop in Figure 4.8).
package client.impl;
import org.osgi.framework.*;
import randomnumberservice.RandomNumberService;
public class Activator implements BundleActivator {
private ServiceReference ref = null;
public void start(BundleContext bctxt) throws Exception
{
System.out.println("client : starting");
// get ServiceReference object

87

Figure 4.8:
randomNumberAct
ivator

88

Chapter 4 General Component Framework Concepts

ref = bctxt.getServiceReference(
"randomnumberservice.RandomNumberService");
// test if this service exists
if (ref != null) {
// get service object
RandomNumberService rns =
(RandomNumberService) bctxt.getService(ref);
System.out.println("client : got service");
// use the service
System.out.println("random number:"
+ rns.getRandomNumber());
}
}
public void stop(BundleContext bctxt) throws Exception
{
// release the service
bctxt.ungetService(ref);
System.out.println("client : service released");
System.out.println("client : stopping");
}

Figure 4.9:
randomNumberCli
ent

The use of services is illustrated by the activator class of the client (see Figure
4.9). The call of the method getServiceReference with the qualified name of the
service yields a so-called service reference. A service reference is like a handle
for a service object and encapsulates the properties and other meta-information
about its service object. The meta-information can be queried by a component to
assist in the selection of a service that best suits its needs. A service reference is
used to ask the bundle context for the corresponding service object. In Figure 4.9,
this is illustrated by the call of getService with the service reference as a parameter. By calling ungetService with the service reference, one can tell the framework
that the client object will no longer make use of the service (see the example in
the stop method of Figure 4.9). Indirect access to services via service references
also enables the OSGi framework to allow services to be of any reference type
(recall from Section 4.2.4 that in TCF, services had to be subtypes of the
tcf.framework.TCFService).
In summary, this paragraph described the structure and syntactical aspects of bundles and how they can provide and access services. The next paragraph introduces
the OSGi container.
OSGi framework. According to the OSGi terminology, the framework includes
the container, that is, the program managing the bundles, and the API for access-

89

Chapter 4 General Component Framework Concepts

ing the container and its services from bundles. It is comparable to the TCF container and framework API, but is much more powerful. In particular, it provides
sophisticated means to dynamically start and stop components, to listen to modifications of the connections between components and services, and to manage different versions of code. Here, we focus on the basic mechanisms for installing,
starting, stopping, and uninstalling bundles.

Figure 4.10:
The states and
transitions of
bundles

Figure 4.10 depicts the states of a bundle within an OSGi container and possible
transitions2:
INSTALLED The bundle has been successfully installed in the container.
RESOLVED All Java classes that the bundle needs are available. This state
indicates that the bundle is either ready to be started or has stopped.
STARTING The bundle is being started, the BundleActivator.start method
will be called, and this method has not yet returned. When the bundle has a
lazy activation policy (specified in the Manifest), the bundle will remain in the
STARTING state until the bundle is first used. This potentially saves resources and initialization time.
ACTIVE The bundle has been successfully activated and is running; its
Bundle Activator start method has been called and returned.

The state transition diagram and description follow the explanation in Section 4.3.2 of the

OSGi specification.

90

Chapter 4 General Component Framework Concepts

STOPPING The bundle is being stopped. The BundleActivator.stop method


has been called but the stop method has not yet returned.
UNINSTALLED The bundle has been uninstalled. It cannot move into another state.
The transitions with the continuous line in Figure 4.10 indicate explicit actions.
The transitions with the dashed line indicate actions automatically triggered by the
container. There are different ways of how explicit actions can be triggered. One
option is to use a command line tool that comes with OSGi implementations. Figure 4.11 demonstrates the use of the command line tool of Equinox. User input is
highlighted in bold.

Figure 4.11:
OSGiSession

$ equinox
osgi> install file:///osgi/plugins/randomNumberService.jar
Bundle id is 2
osgi> install file:///osgi/plugins/client.jar
Bundle id is 3
osgi> start 2
randomNumberService starting
randomNumberService initialized
randomNumberService registered
osgi> start 3
client : starting
client : got service
client : generating a random number: -1065829765
osgi> stop 3
client : service no longer in use client : stopping
client : stopping
osgi> uninstall 3
osgi> close
randomNumberService stopping

The transcript shows:


the startup of the container,
the installation of the random number service and client bundles into the container (in the example, the bundles are installed from the directory
/osgi/plugins) ,
the start of both bundles and their messages,
the stopping and uninstallation of the client bundle,
the closing of the container run.

Chapter 4 General Component Framework Concepts

The command interpreter allows controlling the container and the bundles and
displaying status information and profiling information. Essentially, it provides a
command line interface to the OSGi framework API. That is, the explicit actions
that can be performed using the command interpreter can also be executed from
programs. In particular, bundles can install and start other bundles, and processes
running remotely can control a container.
An important aspect of the OSGi container is that it stores its current state in a
persistent representation when the container is closed. If the container is relaunched, it sets up the state it was in before closing. For example, the close operation at the end of the transcript in Figure 4.11 stops the random number service, but records that the service was running. On relaunch of the container, the
service is automatically restarted. This feature provides a lot of flexibility to compose and connect bundles. On the one hand, the container can be used to resolve
the static dependencies and define a startup order, i.e., almost like a classical
linker and startup tool. On the other hand, all integration and connection operations can be done at a systems runtime; in particular, bundles can dynamically be
updated to new versions.
The following two subsections will describe more details on how bundles and
packages are related and how dynamic service management can be realized.

4.3.3

More about package management and bundles

In addition to requiring and providing services, OSGi bundles structure the program code of component-based systems. Program code is managed in the form of
Java packages. OSGi supports a number of quite complex mechanisms and rules
for package export and import, package identification, class loading and privacy
issues, as well as for handling package versions and version constraints. Here, we
concentrate on the central aspects and illustrate them with a vendor/consumer scenario.
Exporting packages vs. providing services. The bundle randomNumberService
includes two packages. The package randomnumberservice is exported so
that other bundles can import the type of RandomNumberService and use the
bundles service. The implementation package randomnumberservice.impl remains hidden in the bundle. The bundle is an example of a component that exports some program code (namely the interface type of the services)
and provides some implemented service. In this example, the static aspect of the
bundle as a deployable code unit and the dynamic aspect as provider of a service
at runtime fit well.
In general, the relationship between code and provided services is more complex
(cf. also the discussion in Subsection 2.3.4). As a first example, let us consider a
service that should be provided by different components: Into which bundle
should we place the interface type of the service? And if we put it into all bundles,

91

92

Chapter 4 General Component Framework Concepts

how can we make sure that a client of several of these bundles considers all these
types to be the same (that means in Java that they have to be loaded with the same
class loader)? Another issue is code sharing. If several components use the same
code parts (and the functionality of the code cannot or is better not wrapped into
services), it would be a waste of storage resources and dangerous for code maintenance to include a copy of the code in every bundle.
As a solution, the static and dynamic aspects of bundles are decoupled to some
extent. A bundle can provide services without exporting packages. It can export
packages without providing services. It can do both or neither. As this relationship
between static and dynamic aspect is of general interest in CBSD, we illustrate it
with a small example.

Figure 4.12:
A market scenario
to illustrate static
and dynamic
bundle relations

Vendor/consumer scenario. Figure 4.12 shows several vendors (VendorA, VendorB, etc.) that provide a simple service to possible consumers: A consumer can
ask a vendor to make an offer for a certain number of products. The vendor answers with the price or with -1 if no offer is made. The consumer can also ask an
agent to make an offer. The agent tries to determine the best price from among all
vendors. It is clear from Figure 4.12 that vendors provide a service, that agents
require and provide services, and that consumers only require services. Figure
4.12 also shows the export and import relation of packages. Package export is depicted by an upturned square. The bundle Market exports the package market,
the bundle Agent exports the package agent. Package import is depicted by a
dashed arrow. For example, all bundles import the package market. Thus, the two
vendor bundles are examples of components that provide a service, but no code.
Agent is a component providing a service and exporting code. Market only exports code. And the consumers neither provide a service nor export code.

93

Chapter 4 General Component Framework Concepts

package market;
public interface Vendor {
int makeOffer( Product type, int amount );
// result = -1 : no offer is made
// result > 0 : offered price in Cent
}
public enum Product {
BEER, WHEATBEER, PILS
}

To discuss further aspects, we consider part of a prototype implementation of the


above scenario. Figure 4.13 shows the service interface Vendor shared by all
components of the scenario. The parameter type Product is a tiny example of program code that is used by several components without belonging to one of the
components.
The bundle Agent consists of two packages. The exported package agent contains the interface type
public interface Agent extends Vendor { }
and the package agent.impl contains an agent implementation for this interface (see Figure 4.14) and an activator class for the agent (see Figure 4.15). The
prototype implementation is designed to explain technical aspects of OSGi; it is
not meant as a template for professional code (cf. the discussion in Section 4.3.3).
package agent.impl;
import java.util.Set;
import market.*;
import agent.Agent;
public class AgentImpl implements Agent {
Set<Vendor> vendors;
AgentImpl( Set<Vendor> vrefs ) {
vendors = vrefs;
}
public int makeOffer(Product product, int amount) {
int minPrice = -1;
for (Vendor v : vendors) {

Figure 4.13:
packageMarket

94

Chapter 4 General Component Framework Concepts

int offer = v.makeOffer(product, amount);


if ((offer != -1) && ((offer < minPrice)
|| minPrice == -1 )) {
minPrice = offer;
}
}
return minPrice;
Figure 4.14:
classAgentImpl

}
}
package agent.impl;
import agent.Agent;
import market.Vendor;
import java.util.*;
import org.osgi.framework.*;
public class Activator implements BundleActivator {
private BundleContext bctx;
private ServiceRegistration reg;
private AgentImpl agt;
public void start(BundleContext bc) throws Exception {
bctx = bc;
Set<Vendor> vrefs = new HashSet<Vendor>();
try {
ServiceReference[] refArray =
bctx.getAllServiceReferences(
Vendor.class.getName(), null);
// extract the non-agent vendor services
Vendor v;
for (ServiceReference ref : refArray) {
v = (Vendor) bctx.getService(ref);
if (!(v instanceof Agent)) {
vrefs.add(v);
}
}
} catch (InvalidSyntaxException e) {
// never reached because filter is always null.
}
agt = new AgentImpl(vrefs);
reg = bctx.registerService(Agent.class.getName(),
agt, new Hashtable());

95

Chapter 4 General Component Framework Concepts

// for Sect 4.3.3: add listener here


}
public void stop(BundleContext bctx) throws Exception {
if (reg != null) reg.unregister();
}
}

To learn more about some technical aspects of OSGi, it is worthwhile studying the
implementation of the activator shown in Figure 4.15. The OSGi framework provides a method getAllServiceReferences that yields all registered services implementing a certain interface (here market.Vendor). This method can
take a so-called filter as a second argument to select only services with certain
properties. In the example, no filter is given which is the same as a filter that always returns true. The try-catch block catches syntax errors of filter expressions
that might be thrown by the method getAllServiceReferences (not possible in the scenario because of the missing filter). The activator extracts all nonagent vendors from the returned array of service references and passes the set of
vendors to the created agent, which is finally registered at the bundle context, i.e.,
at the container. The following aspects and design decisions should be noted:
The determination of the vendor set happens at agent startup. This only makes
sense if vendors register before the agent and if the set of vendors does not
change over time. In a more dynamic scenario in which vendors come and go,
other techniques are needed (see Section 4.3.3).
The agent gets a set of service objects, not a set of service references (see Section 4.3.1 at the end of the paragraph Bundles for the difference). The advantage is that the agents access to the vendors is faster. The disadvantage is
that property changes of vendors cannot be observed and that the agent cannot
notify the container when a vendor service is no longer needed.
Checking that vendors are not agents themselves is not necessary (and using
the instance of operator for it is bad code and can only be justified by its brevity). Agents could use other agents as well. However, one has to be very careful of not creating cycles. In the given scenario, cycles are not possible. In the
more dynamic scenario of Section 4.3.3, they are possible and would lead to
non-termination of the method makeOffer. It should be noted that such cycles are a general problem in dynamically changing service structures.
Finally, we would like to remark that putting the interface type Agent into the
bundle Agent is only a reasonable design if we are sure that even in the future our
system will not get further agent components. (Our rationale here was to show a
bundle that exports a package and provides a service.)
Summary and beyond. Components in general and OSGi bundles in particular
have two functions: they provide services and manage code. Combining these two

Figure 4.15:
classActivator

96

Chapter 4 General Component Framework Concepts

functions into one unit of abstraction is a characteristic of CBSD. Consequently,


most component frameworks support not only service management, but also provide sophisticated means for code management. For example, the class loading
architecture of OSGi that is based on the Java class loader allows bundles to hide
packages and share packages with other bundles (although every bundle has its
own class loader). Linking importers to exporters is the task of the resolve step,
which takes place after installation (see Figure 4.10). Resolving is a quite complex
process that has to satisfy constraints from the Java language, from Java class
loading, and from the bundle manifests. It can be done for the complete system
prior to startup as with traditional linkers. It can also be done dynamically while
the system is running.

4.3.3

More about dynamic aspects

OSGi provides a very dynamic runtime environment. At runtime that is, when
the OSGi container/framework is running ,
bundles can be installed and resolved, or uninstalled (corresponding to dynamic linking and unlinking of modules);
resolved bundles can be started and stopped (corresponding to loading and
unloading of executables);
services can be registered and unregistered (corresponding to passing pointers
between loaded modules in imperative programming).
OSGi supports a sophisticated infrastructure that enables a bundle to observe
changes in all these categories. Bundle events report on changes in the life cycle
of bundles, service events allow observing services, and framework events cover
aspects like framework startup, framework shutdown, refreshment of packages,
and occurred errors. Bundles can register so-called listener objects for certain
kinds of events. If an event of that kind occurs, the listener object is notified by a
call of a method of the listener interface.
A detailed explanation of OSGi's event infrastructure is beyond the scope of this
textbook. However, since dynamic reconfiguration and plug-in mechanisms are
important applications of component frameworks, we cannot do without considering the basic technique with an example.
Using events. In the vendor/consumer scenario of Section 4.3.2, an agent computed the best offer of a set of vendors. If we generalize this scenario into a system in which vendors can join and leave the market place (like in a Web environment), the agent has to observe these changes. To do so, we register a service listener object with the bundle context. Such an object implements the interface:
public interface ServiceListener
extends java.util.EventListener {

97

Chapter 4 General Component Framework Concepts

public void serviceChanged( ServiceEvent se );


}
When a service is registered, when the properties of a service are modified, or
when a service is unregistered, the service listener is notified by a call of the
method serviceChanged. The service event parameter provides information
about the changes that have happened. With a call to the method getServiceReference, the service event object returns the service reference of the
service that caused the event. With a call to the method getType, the service
event object returns the type of the event that has happened.
To use this mechanism in our scenario, we assume that the agent implementation
of Figure 4.14 is extended by two methods: addVendor adds new vendors to an
agent, remVendor removes a vendor from the current set of vendors; that is,
these methods modify the field vendors in the class AgentImpl.
bctx.addServiceListener(
new ServiceListener() {
@Override
public void serviceChanged(ServiceEvent se) {
Vendor v;
ServiceReference ref = se.getServiceReference();
Object srvc = bctx.getService(ref);
if ((srvc instanceof Vendor)
&& (!(srvc instanceof Agent))) {
v = (Vendor) srvc;
switch (se.getType()) {
case ServiceEvent.REGISTERED:
agt.addVendor(v);
break;
case ServiceEvent.UNREGISTERING:
agt.remVendor(v);
bctx.ungetService(ref);
break;
case ServiceEvent.MODIFIED:
// nothing to do in the considered scenario
break;
}
}
}
}
}

The definition of the service listener and its registration with the bundle context is
depicted in Figure 4.16. It has to be inserted into the activator of Figure 4.15 at the
end of the method start (a comment indicates the insertion point). Thus, we

Figure 4.16:
serviceListener

98

Chapter 4 General Component Framework Concepts

create and register the listener directly after the registration of the service of the
agent. The slight chance that we miss a service event in between the computation
of the vendor set and the registration of the listener is neglected here for the sake
of simplicity. Usually, the service listener is an object of an anonymous class. The
method serviceChanged acquires the service object that caused the event,
checks whether it is a non-agent vendor and, if so, reacts to the change.
Closing comment. The event infrastructure is a powerful high-level mechanism
for coping with all kinds of dynamic changes. However, the complexity of this
mechanism should not be underestimated. In particular in concurrent systems,
managing updates of the service structure and keeping track of references used to
avoid memory leaks is a challenging and error-prone development task.

4.3.4

Further aspects of OSGi

In the subsections above, we have covered central aspects of OSGi, namely the
notion, structure, and life cycle of bundles (Section 4.3.1), the organization of
program code into bundles (Section 4.3.2), and the use of events to observe
changes in OSGi containers (Section 4.3.3). The OSGi framework is more sophisticated in these aspects and provides further features. To illustrate this sophistication, we will briefly discuss some of those issues:
Security: OSGi supports an optional security layer to authenticate bundle
code. It is based on the Java 2 security architecture and provides the infrastructure for deploying and managing applications that must run in finegrained controlled environments.
Versioning: OSGi allows the use of use different versions of a bundle at the
same time. Similarly, it supports the use of different package versions. Import
declarations can declare a precise version for the resolution process. Alternatively, they can declare a version range for matching possible export definitions. OSGi provides a version-matching mechanism as part of the package resolver to handle such constraints.
Container startup and shutdown: OSGi defines a precise procedure for
starting up and shutting down a container (in OSGi terminology, startup and
shutdown of the framework). Between shutdown and startup, it keeps the information about installed and resolved bundles and records all bundles that
were started at shutdown time. Started bundles at shutdown are automatically
restarted on container startup. In addition, all relevant events informing components about shutdown and startup are delivered.
Final remarks. OSGi can be considered as a prototypical language-specific and
application-unspecific component framework. It is based on a detailed notion of
bundles as deployable components. The component model is described well on a
syntactical level. However, the framework provides no support or guideline for

Chapter 4 General Component Framework Concepts

handling concurrency and synchronization issues. This can be interpreted as flexibility for developers who understand all components of the system to be built. It is
a weakness for component developers who do not know the execution contexts in
which their components will be used.
The mechanisms for installing, linking, and starting components and their versions are quite sophisticated in OSGi. On the other hand, OSGi provides only
weak support for selecting and connecting components. Each component has to
manage the connections to required services by itself. In particular, there is no
support for automatically resolving service dependencies.

4.4

Other features of component frameworks

In this chapter, we have presented central concepts of component frameworks and


shown how they are realized with OSGi. A number of important aspects and concepts of component frameworks could not be covered. We will briefly discuss
these in the following paragraphs. Some of them will be treated in greater depth
together with other component frameworks in the following chapters.
Hierarchical components. Frameworks supporting hierarchical components allow the construction of new components by aggregating and enhancing given
components. Hierarchical composition provides better control of the interface of
the new component and better encapsulation of the used components. Hierarchical
component models are well studied in the literature (see, e.g., the SOFA model in
[2]). As an example for studying implementation support for hierarchical components, we refer to the Fractal component model (see [15] for the description of the
component model and http://fractal.ow2.org/ for available implementation support). Other frameworks allow aggregating components into new components using mechanisms that are different from the composition techniques of the framework (a typical example is the component object model COM from Microsoft that
even supports two forms of hierarchical construction, namely containment and
aggregation; see [3], Section 15.2).
Reflexivity. The component model of a component framework should define the
interface to retrieve information from components and operate on them. Such facilities are summarized under the notion of reflexivity. A simple form of reflexivity allows querying the interfaces implemented by a component (e.g., by providing a special interface for such queries; see the interface IUnknown of the COM
framework [3], Section 15.1). Other forms additionally support read and configure
properties of components (see, e.g., the property handling of JavaBeans described
in [3], Section 14.3). The Fractal component model even supports the dynamic
reconfiguration of subcomponents by using interface operations (see [15]).
Multi-language support. Component frameworks may be language-specific (like
OSGi or EJB) or may support the composition and cooperation of components

99

100

Chapter 4 General Component Framework Concepts

written in different programming languages. Multi-language support can be achieved in different ways:
Common IDL: Some component frameworks use a common interface description language, IDL, to express component interfaces in a programming
language independent way. For example, the CORBA Component Model,
CCM, uses the CORBA IDL. So-called language mappings bridge the gap between programming language-specific interfaces and the IDL. More details on
how this mechanism works will be given in Chapter 5.
Common platform: A typical example of a multi-language framework with a
common platform is Mircrosofts .NET framework. It achieves the integration
of components written in different language by providing the so-called common intermediate language, CIL, and the common language runtime,
CLR, for executing CIL code. The CLR also supports a powerful common library. There are compilers for different languages using CIL as the target language.
Common binary standards: Integration can also be done on the machine code
level by defining appropriate standards for executable components and tools
for linking and loading such binary components. This solution allows for high
efficiency, but is usually quite dependent on the underlying computing platform. For example, COM components use this technique.
Multi-language support emphasizes the fact that CBSD covers many aspects that
go beyond programming.
Distribution. In a distributed component system, the components can run in different processes, on different computers, at different sites, under the responsibility
of different organizations. Distribution can be used to increase performance by
deploying the components of a system to several computers. In such scenarios,
distribution is often supported by a load balancing mechanism that starts component instances on those computers with the most resources available. Load balancing can also move running component instances from one computer to another.
Several component frameworks provide support for load balancing (e.g., COM+
from Microsoft).
Distribution is unavoidable if remote partners want to communicate within a
component-based system. For example in the retail store example of Section 1.3.,
some components run on the CashDeskPlatform, while others run on the StoreServer. Distributed component systems need not be under the control of one container, have to handle remote service references, and should organize the data
transport between sites. We will consider these aspects together with the CORBA
in Chapter 5.
Automated connecting of components. In OSGi, the bundle activator usually
establishes the connections of the component instance to its required services (see

101

Chapter 4 General Component Framework Concepts

the client of the RandomNumberService in Figure 4.9). Many modern component


frameworks provide automated support for connecting components. The developer describes the connections in a declarative form, for example as component
annotations or with the help of an XML document, and the component containers
automatically establish the connections. In particular, the containers determine the
order in which the connections have to be established. As this process is controlled by the container and no longer by the components, this change of responsibilities is referred to as inversion of control. As inversion of control exists in
other development scenarios as well, the recent literature uses the more specific
term dependency injection for this mechanism: The container injects the dependencies that a component has on services of other components into the component. We will treat dependency injection together with the EJB framework in
Chapter 6.
Persistence. In many domains and areas, component-based applications have to
manage persistent data. This is mostly done by using relational databases and by
mapping the data state of some of the components onto a relational model. Many
component frameworks support this important aspect. We will explain such support together with the EJB framework in Chapter 6.
Concurrency control. In many component frameworks, support for concurrency
control is weak and the component developers, the application developers, and
even the application deployers have to ensure that concurrency aspects and multithreading do not lead to data inconsistencies or synchronization problems. Some
component frameworks provide support for transactions and control certain concurrency properties. We will consider these aspects together with the EJB framework in Chapter 6.

4.5

Problems for Chapter 4

Problem 4.1:

Problem 4.1:
Two components

Let C1 = <id1,spc1,bdy1> and C2 = <id2,spc2,bdy2> be two components. When


are C1 and C2 considered to be equal? Is it sufficient that the specification is the
same?

Problem 4.2:
Explain the main composition mechanisms of component frameworks.

Problem 4.3:
A bundle can be in different states in the OSGI container. What corresponds to the
transition from INSTALLED to RESOLVED in traditional software construction?

Problem 4.2:
Component
frameworks

Problem 4.3:
States of a bundle

102

Chapter 4 General Component Framework Concepts

Problem 4.4:
Static and dynamic
aspects of bundles

Problem 4.5:
Services

Problem 4.4:
What are the static and dynamic aspects of bundles? How are they related?

Problem 4.5:
Components provide and use services. We say that component C1 depends on C2
if C1 uses a service provided by C2. How can this dependency relation be cyclic?
Can a component observe whether it is part of a cycle? What are the problems of
cyclic dependencies?

Chapter 5 Distribution and Heterogeneity in Component Frameworks

Distribution and Heterogeneity in Component


Frameworks

In general, component-based software systems have to run on multiple machines


in several containers with components written in different languages. An example
of such a system is the retail store system sketched in Chapter 1 running on the
cash desk platforms and the store server. In this chapter, we present approaches to
realizing such distributed and heterogeneous systems. We first focus on the problems and concepts of distribution. Then, we briefly discuss aspects of heterogeneity. The main section introduces CORBA and CCM, the framework solution of
the Object Management Group for developing and deploying distributed and heterogeneous systems.

5.1

Learning objectives of this chapter

After studying this chapter, the reader should


understand the problems of distribution and heterogeneity,
know the general mechanisms and techniques for handling distribution and
heterogeneity in component systems,
be able to explain the main parts of the CORBA framework,
be able to read and understand components developed based on the CORBA
framework,
be able to describe the main aspects of the CORBA component model.

5.2

Distribution: problems and concepts

In this section, we present and discuss the general problems and concepts related
to the development of (distributed component-based systems). The basic picture
is that there are two or more components running in different operating system
processes on one or several computers. Such scenarios can be found in embedded
systems like cars or airplanes, in application-specific communication systems, as
well as in information systems.
We assume here that the communication infrastructure between the processes abstracts from communication faults in the following way: Either the communication works correctly or the communication partners are notified that an error has
occurred. That is, error detection and mechanisms for fault tolerance are considered to be part of the infrastructure. In general, the infrastructure can support different kinds of communication:
Remote procedure call (RPC)

103

104

Chapter 5 Distribution and Heterogeneity in Component Frameworks

(Remote method invocation (RMI) on an object reference


Synchronous and asynchronous messages
Streaming of information over a channel
In the following, we concentrate on remote method invocation. Remote method
invocation (RMI) allows calling methods on references of objects outside the local process. RMI is more general than remote procedure calls, because the caller
does not have to know where the receiver object is. Thus, a remote method invocation can be transparent with regard to the site that executes the call. More important, RMI directly supports the notion of services as discussed and used in the
last chapter. Some components can register a service by yielding a reference to an
object that provides the service. Other components can use the service via the reference.

Figure 5.1:
A remote object S
registered at some
registration process

Figure 5.2:
A client object
connected to a
remote object

Chapter 5 Distribution and Heterogeneity in Component Frameworks

Concepts of RMI. Figures 5.1 and 5.2 illustrate how RMI works. Processes providing services have to register their services. In the depicted scenario of Figure
5.1, the server process has registered the remote object S at the registry. Object S
provides a service with the name aService. Such an object is sometimes called a
servant (for example in CORBA). In order to perform the registration, the RMI
framework has to provide a mechanism for registering a remote reference under a
name. In a Java environment, the registration mechanism and the handling of remote object references is directly managed by the Java Virtual Machine. In
CORBA, the registration mechanism is provided by the naming service of the socalled object request broker; object references are exported as so-called Interoperable Object References (IOR). On the server side, the servant is represented by a
so-called skeleton.
If a client wants to use a remote object or service, it has to look up the corresponding object reference under a name at the registry. To do so, the client object talks
to the lookup mechanism of the RMI framework in its own process and receives a
process-local reference to a so-called stub. The stub represents the remote object
within the client process. In particular, it provides the same methods as the public
interface of the servant. The client can invoke methods on the stub and pass the
stub reference to other objects.
To transfer arguments between stub and skeleton, objects and values have to be
handled in a process-independent form. In Java, for example, local objects are serialized and remote objects are coded in a special way. Transforming arguments
into a process-independent representation is called marshaling. The reverse transformation is called unmarshaling. In RMI, the stub marshals the parameters and
transfers them to the skeleton. The skeleton unmarshals the parameters, calls the
method on the servant, accepts the result, marshals the result, and sends it back to
the stub. Finally, the stub returns the unmarshaled result to the client object.
RMI has some similarities with service registration in OSGi (cf. Section 4.3). As
in local settings, client components do not need to know the components providing the service. However, the distribution of the registration and lookup mechanism to several processes has a number of technical and even semantic implications:
Handling of remote references
Passing of parameters and results
Method binding and type checking
Managing program code
Handling of errors
We will explain and discuss these conceptual aspects in the following paragraphs.
They are the starting point for understanding, developing, and using RMI. As

105

106

Chapter 5 Distribution and Heterogeneity in Component Frameworks

most component frameworks build on object orientation and RMI for distribution,
the concepts of RMI are also essential for CBSD.
Handling of remote references. Process-local object references can be represented by a memory address or some identifier derived from addresses. Outside
the process, such a representation is
not sufficient, because references in two different processes might be represented by the same address;
not practical, because once an address is passed out to a remote process, objects can no longer be moved in the (local) address space, which is too restrictive for most runtime systems with garbage collection.
To be used from remote sites, the representation of an object reference has to
identify the object within its process, the process on its machine, and the machine
within the Internet or some other name space. Based on such a representation, a
remote reference can be passed around from process to process. For example, let
us assume that the client process in Figure 5.2 has a reference to another remote
servant T. It could pass the reference of S as a method parameter to T when invoking a method on T. This way, an object can receive references to objects in
many other processes.
Frameworks for distributed object-oriented computing or distributed component
systems differ in the way they represent remote references and in the operations
and forms of equality they support for remote references.
Passing of parameters and result. Passing primitive data values, like integers,
characters and Booleans, as parameters or result is only a coding problem. Design
decisions have to be made when objects are allowed as method parameters (the
same is true for objects as result). Essentially, there are two options:
1. Pass objects by reference, that is, create a remote reference for the parameter
object and pass it to the servant.
2. Pass objects by copy, that is, serialize the parameter object and all objects directly or indirectly referenced by it and create a copy of the parameter object
and the object structure referenced by it within the process of the servant.
The main disadvantage of calls by reference is that every method call of the servant on a parameter, even on a string object or an array, becomes a remote call.
This is very expensive. The main disadvantages of the second option are that potentially large object structures have to be copied (which is expensive) and that
object identity on the parameter objects is broken: If the servant modifies the parameter objects, the original copies within the client process remain unchanged.
Thus, the second option has semantics that are slightly different from local
method invocation semantics. Most frameworks allow the component developer
to combine both options in order to achieve good performance.

Chapter 5 Distribution and Heterogeneity in Component Frameworks

Method binding and type checking. As we have seen in Section 4.3, OSGi containers perform type resolution at deployment time. When a new bundle is installed, the container figures out which types of the new bundle are the same as
types already available in the container and which are different. In a distributed
scenario, type resolution and compatibility checking become part of the lookup
and the invocation mechanisms. In many frameworks, the server side dynamically
checks the types of the parameters and the client side checks the type of the result.
Managing program code. Object-oriented distributed programming and component development have to deal with another non-trivial problem that is best explained with an example. Let us consider a servant providing the following simple
service interface:
public interface CheckerService {
public boolean check( Offer o );
}
The method checks whether an offer has a certain property. It does so by calling
methods on the Offer object. We assume here that Offer is an interface type.
When a remote client uses the service, the parameter should be copied and created
on the servant side. But what happens if the classes needed to deserialize the
passed parameter are not available at the servant side? The implementation of the
checker service can be compiled based on the interface type; it does not need access to classes implementing the type Offer (and even if the servant knows some implementations of Offer, some future client may pass an Offer object that
is an instance of a class that is unknown to the checker service implementation).
To overcome this problem, a framework for distributed object-oriented component development needs support for loading new classes and components at runtime from remote sides, that is, for dynamic automated deployment. Without going into details, it should be clear that such mechanisms can become quite complex and may cause difficult security problems.
Handling of errors. All of the above conceptual distribution aspects are a source
of errors and exceptions. In addition, there are errors and exceptions that are
caused by the underlying communication infrastructure. Whereas in an ideal scenario, developers might tend to treat distribution issues as transparently as possible, the realistic approach taken by most of the frameworks today is to expose the
component, application, and system developer to the errors and exceptions that
might be caused by distribution. Thus, although a remote method invocation is in
many respects like a local method invocation, and although the communication
infrastructure abstracts from many low-level aspects, development of distributed
systems still needs additional knowledge and care.
Further aspects. The goal of this section was to present the basic conceptual aspects of distributed object-oriented computing before explaining how they are
handled by particular frameworks and technologies. Beside these basic aspects,

107

108

Chapter 5 Distribution and Heterogeneity in Component Frameworks

there are other interesting aspects that come up in a distributed environment. In


particular, a distributed system allows for load balancing between different sites to
improve performance and poses new challenges for locating the best service for a
given task in a network. Compared to local settings, the main difference with regard to locating services is that there is not one central registry, but the registries
are distributed to many sites (possibly to sites that are not known at deployment
time).

5.3

Heterogeneity: multi-language development for systems


running on different platforms

Distribution and integration of components of different development organizations cause another challenge. The computing platforms underlying a distributed
system can be heterogeneous in terms of the hardware, the operating system, and
other platform aspects. Furthermore, different developers might use different programming languages for implementing components. We distinguish two different
forms of how heterogeneity is solved:
1. Direct composition, i.e., heterogeneous components can be directly linked
and connected.
2. Brokered composition, i.e., heterogeneous components are linked to a broker
that mediates between components.
To handle direct composition, we need standards that allow linking and loading
compiled components written in different languages. These standards can be defined on the level of machine or assembler code, or they can be established on the
level of virtual machine code (as in Microsofts .NET framework; cf. Section 4.4).
Technology for direct composition has to enable the linking of component code
and the connection of component instances. In particular, a component written in
one language can directly call a method/function of a component written in another language. The main advantage of direct composition is that it is more efficient than brokered composition. The main disadvantage is that components can
easily corrupt other components.
In brokered composition of heterogeneous components, the communication between components is mediated by an intermediate layer. A component written in
one language talks to its broker. The broker manages the communication with other components. In particular, the broker can convert different representations of
data types and handle references between components. Brokers can connect components within one process or components distributed across different platforms.

109

Chapter 5 Distribution and Heterogeneity in Component Frameworks

module ExampleModule {
enum Product { BEER, WHEATBEER, PILS };
interface Vendor {
long makeOffer( in Product type, in long amount );
};
};

Whereas direct composition has to define the standards for naming, data types,
references, method calls, and communication on the level of (virtual) machine
code, brokered composition can base the integration on a high-level interface
definition language or IDL. For example, Figure 5.3 shows a definition of the
interface between vendor and consumer components according to the vendor/consumer scenario of Section 4.3.2 in the CORBA IDL (compare to the Java
version in Figure 4.13). As the syntax is very similar to Java, the definition should
be clear; the keyword "in" in front of parameters indicates that the parameter is
an input to the method (CORBA IDL also supports output parameters).
To integrate different programming languages (e.g., C and Java), frameworks
based on brokered composition realize so-called IDL mappings for each of these
languages. An IDL mapping for a language L specifies how an interface defined
in IDL corresponds to an implementation defined in L. Usually, a mapping for a
language L comes with a compiler that generates declarations in L from interface
definitions in IDL. The component developer uses these language-specific interfaces to implement components in L.
More aspects of brokered composition will be explained together with the introduction to CORBA and the CORBA Component Model in the following section.

5.4

Common Object Request Broker Architecture

The Common Object Request Broker Architecture (CORBA) is a vendor-neutral


standard for distributed object-oriented computing defined by the Object Management Group (OMG). The OMG, founded in 1989, is the largest consortium in
the information technology industry. It is a non-profit organization for standardization to achieve interoperability on all levels of software development.
CORBA enables software systems written in multiple computer languages and
running on multiple computers to work together. Up to version 2, CORBA did not
support a well-defined notion of components. It was a framework for distributed
heterogeneous object-oriented computing. Version 3 extended CORBA by the
CORBA Component Model, CCM, adding typical features of component frameworks.

Figure 5.3:
moduleMarket

110

Chapter 5 Distribution and Heterogeneity in Component Frameworks

As described in [29], Section 2.7, CORBA is used in everything from billing systems and multi-media news delivery to airport runway illumination, aircraft radio
control and the Hubble space telescope. Most of the worlds telephone systems, as
well as the truly mission-critical systems operated by the worlds biggest banks,
are built on CORBA. Applications and success stories of CORBA can be found,
for example, at www.corba.org.
This section explains the main parts of CORBA and focuses in particular on language independency and the object broker infrastructure. We first sketch the basics of the architecture (Section 5.4.1), then demonstrate the development of a
simple multi-language distributed system with CORBA (Section 5.4.2), summarize further aspects of CORBA (Section 5.4.3), and explain the CCM (Section
5.4.4).

5.4.1

Basics of the Common ORB Architecture

The basics of CORBA can be understood as a refinement of the RMI mechanisms


explained in Section 5.2 (cf. Figure 5.2). It allows client objects to invoke methods on remote servant objects. The implementations of clients and servants might
be written in different languages and might run on different machines with different operating systems.

Figure 5.4:
The simplified
runtime view of
CORBA

Figure 5.4 gives an overview of the Common ORB Architecture. To simplify the
wording in the following explanations, we call the process of the client objects the
client process and the process of the servants the server process, respectively.
However, it is important to understand that in general, a process can contain client

Chapter 5 Distribution and Heterogeneity in Component Frameworks

objects and servants at the same time and that its client objects can communicate
with remote servants and the servants can provide services for remote clients.
CORBA concepts. The Object Request Broker, ORB for short, together with its
services provides the functionality of a distributed container infrastructure. Technically, the ORB is often realized as library code that is linked to the client and
server processes. It is accessed by clients and servants through a large API. (In
some respects, the ORB corresponds to the TCF or OSGi frameworks described in
Chapter 4.)
Compared to RMI, the relationship between clients and servants is more refined in
CORBA. In RMI, a remote reference directly corresponds to a servant object. In
CORBA, there is an additional indirection layer between the client and the servant. The indirection is handled by the so-called object adapter (see Figure 5.4).
The reason for this indirection is to increase the flexibility and power of the
framework. In CORBA, it is possible, for example, that a server process is
stopped after it has registered a service at a registry process. This is important in
order to achieve scalable performance in large systems. If a client wants to use the
service, it can look up the service at the registry, obtain a reference R, and invoke
a method on R. Note that at this point, the server process is still down and there is
no object that is referred to by R. If configured appropriately, the method invocation on R leads to an automatic restart of the server process. A new servant object
is created and made available to the client. That is, the reference stored in the registry only establishes an indirect, dynamic correspondence to servant objects.
To achieve its flexibility, CORBA provides so-called Interoperable Object References, IORs for short. An IOR is a reference to a so-called CORBA object. IORs
can be stored in files or registries, can be compared, passed around among processes, and used to invoke methods. However, as sketched in the scenario above,
the referenced CORBA object does not correspond to a "physical" servant object
in a running process. It can be represented by different servant objects (over time,
but as well at the same time). A CORBA object is called transient if it has a
shorter lifetime than the server process. Otherwise, it is called persistent.
The so-called object adapters manage the relationship between IORs and servants/skeleton. Object adapters provide a lot of flexibility. In particular, an object
adapter can manage several servants. In the first versions of CORBA, the socalled basic object adapters were only vaguely described by the standard. Newer
CORBA versions standardize so-called portable object adapters, POAs, for short.
In the following, we only consider POAs. The object adapters support different
policies for transient and persistent objects.
To realize mechanisms for shutdown and startup of server processes, CORBA
supports so-called implementation repositories, IMRs. As indicated in Figure
5.4, an IMR runs in a separate process and can manage many server processes. If
an IOR R refers to a CORBA object in an IMR-managed server and this server is

111

112

Chapter 5 Distribution and Heterogeneity in Component Frameworks

down, an invocation on R triggers the IMR to start the server and to redirect the
IOR communication to an appropriate, newly created object adapter.
Concepts for multi-language support. CORBA server and clients can be implemented in different languages. To achieve this, CORBA standardizes a common, programming language independent IDL to describe the interfaces between
clients and servers and language mappings to C, C++, Java, Ada, Smalltalk, COBOL, PL/I, LISP, Python and IDLScript; further non-standardized language mappings exist. To support some programming language PL, a CORBA implementation has to provide:
an implementation of the CORBA framework in PL and
a compiler from IDL to PL.

Figure 5.5:
A multi-language
compilation
scenario with IDL

Figure 5.5 illustrates a multi-language scenario in which the client is written in


C++ and the server in Java. The IDL compilers translate the IDL declarations into
C++ and Java code for stubs and skeletons. Of the generated C++ code, only the
client stub is needed (that is why the arrow to the skeleton is depicted with a broken line). It is a class providing the methods with the same signatures as in the
service interface. The client code uses these methods to access the server. Of the
generated Java code, only the realization of the skeleton is needed (that is why the
arrow to the stub is depicted with a broken line). It is combined with the implementation of the servant. The following subsection demonstrates application development with CORBA in more detail (for further details, see [14]).

Chapter 5 Distribution and Heterogeneity in Component Frameworks

5.4.2

113

Development of CORBA-based systems

In the following, we sketch how to develop distributed object-oriented systems


with CORBA. As a running example, we use a modified market scenario: Different vendors can register at a market and offer their products. Consumers can ask
the market for all currently registered vendors and ask the vendors to make an offer. Figure 5.6 illustrates this scenario with two vendors, one consumer, and one
market process (registry processes are omitted). To demonstrate the use of multiple languages, we discuss two vendor implementations, one in Java and one in
C++.

Figure 5.6:
The processes in
the market scenario

The first aspect that should be noted in the market scenario of Figure 5.6 is that
the client-server relationships among the processes are more interesting than in
the basic configuration we have discussed so far. Vendors are clients to the market
when they register via registerVendor. The market is a server for both the
vendors and the consumer (getAllVendors). The vendors are servers for the
consumer when the consumer asks them to make on offer.
module MarketIF {
enum Product { BEER, WHEATBEER, PILS };
interface Vendor {
long makeOffer( in Product type, in long amount );
};
typedef sequence<Vendor> VendorSeq;
interface Market {

114

Figure 5.7:
moduleMarketServ
er

Chapter 5 Distribution and Heterogeneity in Component Frameworks

void registerVendor(in Vendor v);


VendorSeq getAllVendors();
};
};

The interfaces and types for the market scenario are defined in the IDL module
shown in Figure 5.7. IDL can declare interface types similar to Java interfaces and
several other kinds of types, like enumerations and collection types. From the
module MarketIF, the IDL-to-Java compiler that we used3 generates packages
with 19 class and interface declarations. The IDL-to-C++ compiler that we used4
generates a 790-line C++ file and a 275-line header file.
As it is beyond the scope of this book to explain the details of the language mappings, we only describe those aspects that are needed to develop simple CORBAbased systems. In the following, we explain how to implement clients (using the
consumer as an example) and servers (using the market as an example). We
briefly discuss implementations in different languages (using vendors as an example) and finish with some remarks about deployment and startup.
import
import
import
import

MarketIF.*;
// imports the IDL generated types
org.omg.CosNaming.*;
org.omg.CosNaming.NamingContextPackage.*;
org.omg.CORBA.*;

public class Consumer {


public static void main(String[] args) {
try {
// Create and initialize the ORB
ORB orb = ORB.init(args, null);
// Get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef =
NamingContextHelper.narrow(objRef);
// Resolve the object reference in naming
NameComponent[] path =
{ new NameComponent("Market","") };
Market marketRef =
MarketHelper.narrow(ncRef.resolve(path));
// Use the Market object

3 We used the JacORB IDL compiler V 2.3.1.


4 We used the MICO compiler V 2.3.13

115

Chapter 5 Distribution and Heterogeneity in Component Frameworks

Vendor[] vendors = marketRef.getAllVendors();


for (Vendor v : vendors) {
System.out.println(v.makeOffer(Product.PILS,40));
}
} catch(Exception e) { ... }
}
}

Client development. In the client, the main difference between a local and a distributed scenario is the access of the remote reference. In the case of the consumer, it is the problem to get the IORs of the market and of the vendors. Figure
5.8 shows the needed steps:
1. First, we have to initialize the ORB.
2. From the ORB, we can get a reference to a name service, that is, to a remote
object that maps names to IORs. The reference returned by the method resolve_initial_references has to be cast down to the type NamingContext. These downcasts are realized by methods called narrow, which are
defined in appropriate helper classes.
3. The naming context provides a method resolve returning the IOR stored
under a name: Names may consist of several components; that is why they are
represented by arrays.
4. The reference returned by the method resolve has to be narrowed to the
appropriate type. In our case, this is the type Market. Note that the types Market and MarketHelper are generated from the IDL module, whereas the other
types mentioned so far are part of the CORBA framework.
The reference of the type Market can be used as if it were a reference to a local
object. For example, it can be used to call the method getAllVendors. This
method returns a vendor array because the IDL sequence type is mapped to Java
arrays. It is important to note here that the vendor references stored in the array
are IORs and that these IORs may refer to objects in processes different from the
process of the client (i.e., the consumer process) and server (i.e., the market process ).
import MarketIF.*;
import java.util.*;
class MarketImpl extends MarketPOA {
private Set<Vendor> vendors = new HashSet<Vendor>();
public void registerVendor(Vendor v) {
vendors.add(v);
}

Figure 5.8:
consumerJava

116

Chapter 5 Distribution and Heterogeneity in Component Frameworks

public Vendor[] getAllVendors() {


return vendors.toArray(new Vendor[0]);
}

Figure 5.9:
marketJava

Server development. The development of a server consists of the servant implementation and of a program that creates the servant and makes it accessible to other processes. We illustrate the steps with an implementation for the market server.
Whereas the stub class generated for an IDL interface MyService has the same
name, the generated skeleton class is an abstract class named MyServicePOA (in
the Java language mapping). The name is definitely misleading, as objects of such
classes are not portable object adapters (POA). A class implementing servants is
typically realized as a subclass of the skeleton class. It is usually called MyServiceImpl. In the Java language mapping, neither MyServicePOA nor MyServiceImpl have to be a subtype of MyService; however, MyService has to implement all methods of the interface MyService. Figure 5.9 demonstrates the servant
implementation for Market.
import
import
import
import
import

MarketIF.*;
java.util.*;
org.omg.CosNaming.*;
org.omg.CosNaming.NamingContextPackage.*;
org.omg.CORBA.*;

public class MarketServer {


public static void main(String[] args) {
try {
// Create and initialize the ORB
ORB orb = ORB.init(args, null);
// Create and activate a POA
org.omg.PortableServer.POA poa =
org.omg.PortableServer.POAHelper.narrow(
orb.resolve_initial_references("RootPOA"));
poa.the_POAManager().activate();
// Register the servant with the POA and record IOR
org.omg.CORBA.Object markRef =
poa.servant_to_reference( new MarketImpl() );
// Get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");

Chapter 5 Distribution and Heterogeneity in Component Frameworks

117

NamingContext ncRef =
NamingContextHelper.narrow(objRef);
// Bind the object reference in naming context
NameComponent[] p = {new NameComponent("Market","")};
ncRef.rebind( p, marketRef );
// ORB waits for incoming requests
orb.run();
} catch(Exception e) { ... }
}
}

The steps needed to create and register an IOR for a servant are illustrated by the
class MarketServer in Figure 5.10. The steps shown are:
1. Create and initialize the ORB.
2. Create and activate a portable object adapter.
3. Register a servant object with the POA. The shown operation servant_to_reference returns the IOR for the servant.
4. Get the (root) naming context of the ORB.
5. Bind the IOR of the servant to an appropriate name. The method rebind is
used to overwrite an existing binding with the same name.
6. Start the ORB loop that listens to incoming requests.
Of course, CORBA provides many other options and variants to start up processes
participating in distributed systems. For example, a vendor server does not register its vendor(s) with a naming service, but registers them without a name at the
market (or even at several markets).
import MarketIF.*;
class Vendor1Impl extends VendorPOA
{
public int makeOffer( Product type, int amount ) {
if( (type==Product.PILS) && (50 >= amount) ) {
return 200 * amount;
} else {
return -1;

Figure 5.10:
marketServerJava

118

Chapter 5 Distribution and Heterogeneity in Component Frameworks

}
}
Figure 5.11:
vendorServerJava

}
#include <CORBA.h>
#include "MarketIF.h"
using namespace CORBA;
using namespace MarketIF;
class VendorImpl : virtual public POA_MarketIF::Vendor
{
public:
virtual Long makeOffer(Product type, Long amount );
};

Figure 5.12:
vendorCPP

Long VendorImpl::makeOffer(Product type, Long amount )


{
if( (type == PILS) && (50 >= amount) ) {
return 200 * amount;
} else {
return -1;
}
}

Aspects of language mappings. The language mappings to object-oriented programming languages are quite similar. Figures 5.11 and 5.12 illustrate the similarities with a Java and C++ implementation for vendor servants. The main differences are how the IDL types are mapped to the programming language types and
how the module structure of the CORBA framework is expressed by means of the
programming language constructs. For target languages without support for objects, the mappings are more involved.
Deployment and startup. CORBA does not provide standardized mechanisms
for the deployment of client or server code. The technical details of deployment
depend on the CORBA implementation used, on the underlying programming environments, the platforms, and the communication protocols of the used networks.
Here, we briefly describe the general aspects shared by all CORBA implementations and platforms.
The deployment of clients consists of the client code itself and the library part of
CORBA (the development part of CORBA, in particular the IDL compiler, does
not need to be available). Furthermore, appropriate configuration and log files
could be provided that configure parameters of CORBA to yield diagnostic information. When starting a client process, one should make sure that the server

Chapter 5 Distribution and Heterogeneity in Component Frameworks

side is in an accessible state (e.g., already running or capable of being started


automatically).
The deployment of servers consists of the server code, the library part of CORBA,
and the deployment of the IMR if the IMR mechanism is to be used (see Section
5.4.1 for an explanation of IMR). Essentially, there are four options to deploy a
server (we assume here that communication is based on TCP/IP and that the operating system of the host machine supports fixed and random ports):
1. The server listens at a random port and is deployed without IMR.
2. The server listens at a random port and is deployed with IMR.
3. The server listens at a fixed port and is deployed without IMR.
4. The server listens at a fixed port and is deployed with IMR.
Depending on the chosen option, servers have to be always available or can be
started and restarted on demand. The chosen option also has to be compatible with
the selected POA policy. As an explanation of the different options is beyond the
scope of this book, we sketch here only the first option, which can be considered
as the simplest case. The option is applicable if a server only has transient objects
(see Section 5.4.1 for an explanation of transient), i.e., if all objects belong to
POAs with a transient policy. The random port that the server is listening at is
chosen by the operating system when the server is started. The chosen port is part
of the IORs returned by the server process. Consequently, the IORs can only be
used during the lifetime of the server process (at the next startup, a different port
might be assigned to the server process). To ensure that IORs are valid across restarts of the server, one has to either use fixed ports (option 3) or work with IMRs.

5.4.3

CORBA infrastructure and services

CORBA standardizes the core mechanisms presented above and the needed languages and formats. This includes the IDL, which is much more elaborate than
demonstrated. In particular, it provides many additional forms of type declarations, such as so-called value objects, and support for versioning. The CORBA
standard defines the formats for IORs, the CORBA URL formats, details on marshaling parameters, and aspects of communication protocols. In addition to this, it
describes a number of infrastructure features and services supported by the
CORBA framework. As an overview, we list the more important features and services in the following and discuss those that are particularly relevant for CBSD.
The CORBA infrastructure provides portable interceptors to be called at creation
time of IORs and whenever a request is made on an IOR. Interceptors can be
used, for example, to log profile data for an IOR. Furthermore, the CORBA infrastructure supports mechanisms for fault tolerance, reflection, and specific forms of
message handling (the latter two mechanisms are discussed below). In addition to

119

120

Chapter 5 Distribution and Heterogeneity in Component Frameworks

the infrastructure, CORBA makes a number of services available, in particular a


trading service, a transaction service, a publish and subscribe service, and the
CORBA security service. We discuss the trading service below.
Reflection capabilities. Reflection allows the programmer to ask objects at runtime about their type information and the interfaces they implement and to use the
interface information to invoke methods. Reflection capabilities are not only supported by CORBA, but by most modern languages (Java, for example, provides
reflection via the standard library packages java.lang and
java.reflection). They play an important role in implementing component
frameworks. As an example where reflection is needed, we consider the realization of a test client that allows inspecting all methods provided by a received, arbitrary IOR and invoking one the IOR's methods. When such a test client is developed, the IDL interface types for all existing and future IORs are not available.
Thus, we cannot use the stub classes for these types and cannot invoke the methods by an ordinary method call.
To enable reflection, CORBA provides an infrastructure to store, retrieve, and use
type information at runtime. Simple type information, the so-called TypeCode,
can be stored together with the special IDL type any. The type any is the root
type of CORBA's type hierarchy. That is, every CORBA object is of the type
any. However, the TypeCode does not include information about the methods
provided by an interface type. To query an interface type for its methods, this information has to be stored in a so-called Interface Repository. An Interface Repository (IFR) is a database of meta-information about IDL types that is accessible as a CORBA service. To access information in an IFR, CORBA provides the
so-called Dynamic Invocation Interface (DII) for the client side and the Dynamic Server Interface (DSI) for the server side.
As an example of how these mechanisms are used together, we sketch the conceptual realization of the test client. In order to inspect and invoke the methods of an
IOR, the interface information of the type of the IOR has to be stored in an IFR
that the test client can access. This is done by the developer of the CORBA object
referenced by the IOR. Note that the interface information can be stored after the
client has been developed or started. The DII allows the client to access the interface information of the IOR in the IFR. Using other mechanisms of the DII not
explained here, the client can marshal the arguments for a method invocation, invoke the method on the IOR, and unmarshal the results.
CORBA messaging. As we have seen, a remote method invocation consists of
several steps, many of which can fail. For example, the server may be down when
the call is made, the client may be down when the call returns, or the network
transmission may cause an unacceptable delay. CORBA provides mechanisms
that allow the programmer to describe how she wants a certain situation to be
handled. She can define quality of service policies for the communication; she
can, for example, define a timeout value for calls.

Chapter 5 Distribution and Heterogeneity in Component Frameworks

The Asynchronous Messaging Interface (AMI) enables non-blocking invocations. To retrieve the results of a non-blocking invocation, AMI supports callback
objects. The caller of a non-blocking invocation can continue its execution after
the invocation and later retrieve the results from the callback object. (What is
called a callback object in CORBA is similar to what is often called a future in
the programming literature.)
CORBA also provides an infrastructure for so-called Time Independent Invocations (TII). If a client is down when a method call returns, TII keeps the reply
message in some form of persistent storage until the client is restarted. Then, the
reply message is delivered. The TII mechanism is built on so-called method
routers, which ensure that the message is stored persistently until it can be delivered.
Trading service. CORBA does not prescribe how a CORBA object is made accessible to clients. It provides several options for how this can be done. If client
and server have access to the same file system, a server can simply store the IOR
in a file from which the client reads the IOR. In Section 5.4.2, we have illustrated
how a CORBA object can be made accessible using CORBA's Naming Service.
The server registers the IOR at the naming service by binding it to a name. Clients
can look up the IOR under the name. The market object in the market scenario of
Section 5.4.2 demonstrated a (trivial) programmer-defined service for making the
CORBA vendor objects accessible to consumers.
Through its trading service, CORBA provides a sophisticated and powerful
mechanism for advertising services by its properties. More precisely, the trading
service builds on:
The ServiceTypeRepository, a repository of service offer types: A service offer type describes the properties of a class of services, for example all printing
services on postscript printers. It is given by a name, a CORBA (super-)type
of the service to be offered, and properties that the offered service must have.
The repository stores such service offer type descriptions and allows adding
new service offer types and removing existing ones.
The Register mechanism for registering a service with certain properties for a
service offer type T. A service may only be registered for T if it has at least
the properties of T. It is import to understand that many services can be registered for the same service offer type T. (This is one main difference to the
naming service that associates a name with at most one service.)
A Lookup mechanism for finding the service that is the best match for a specified constraint. Constraints are formulated as Boolean expressions over the
properties defined in the service offer type. CORBA provides a special language for these expressions, the so-called Trader Constraint Language
(TCL).

121

122

Chapter 5 Distribution and Heterogeneity in Component Frameworks

The CORBA trading service also provides mechanisms for joining several Trading Services so that a query to one trading service can be propagated to other services. From a conceptual point of view, the trading service infrastructure is interesting, as it illustrates the basic features needed for CBSD scenarios with automated component selection based on component properties.

5.4.4

CCM: The CORBA Component Model

CORBA is a framework for the development of distributed object-oriented systems. Up to Version 3.0, CORBA did not support a component model and the deployment of components. Starting with Version 3.0, CORBA was complemented
with a component model, the so-called CORBA Component Model, CCM for
short. CCM was originally part of CORBA 3.0 and is now a separate specification
(version numbers start with 4.0). CCM has a number of similarities with component frameworks for application servers. However, by supporting language and
platform independency, it goes beyond many of these frameworks. The shared
conceptional aspects of component frameworks for application servers will be
presented together with Enterprise Java Beeans in Chapter 6. Here, we briefly
sketch aspects specific to CCM.

Figure 5.13:
CCM component
with ports and
attributes

Component interfaces and descriptions. Figure 5.13 illustrates the different


kinds of ports a component can have. CCM ports are method or event interfaces.
Provided method interfaces are called facets, required method interfaces are
called receptables. There is one special provided interface called the Equivalent
Interface. The Equivalent Interface allows clients of the component to navigate
among the components facets and to connect to the components ports. The event
interfaces are used by a component to send and receive events to and from other

123

Chapter 5 Distribution and Heterogeneity in Component Frameworks

components. Events are a mechanism for asynchronous communication. Attributes are named configurable component properties. They are intended to be configured at assembly, packaging, deployment, or instantiation time. In addition to
these interfaces, a component has a home interface for component instantiation
(for an explanation of home interfaces, see Chapter 6).
CCM supports four different component categories. Service Components are
stateless components that are used to model sessions. They do not provide a key
for persistent access. Session Components have a state, but no key. Process
Components have a persistent state that is not visible to clients, a persistent identity, and a (potentially transactional) behavior. Entity Components have a persistent state that is visible to clients through a primary key and a (potentially transactional) behavior (for an explanation of home interfaces, we again refer to Chapter
6).
module producerScenario {
enum ProductKind { ... };
interface Product { ... };
interface ProducerIF {
Product getProduct();
};
eventtype ProductEvent {
public ProductKind prodKind;
};
component Producer supports ProducerIF {
provides ProducerIF
providesProduct;
publishes ProductEvent productEventSource;
};
component Consumer
uses
ProducerIF
requiresProduct;
consumes ProductEvent productEventSink;
};
}

To achieve language independency, CCM provides a programming-languageindependent so-called Component Implementation Definition Language,
CIDL. CIDL is used to describe the structure and state of component implementations. In particular, it allows defining the interfaces of components and connecting
ports of different components. Figure 5.14 gives an idea of component interface
definitions. A Producer component provides an interface (facet) for retrieving a

Figure 5.14:
cidlExample

124

Chapter 5 Distribution and Heterogeneity in Component Frameworks

product and an event source that sends out a product event whenever a new product is finished. The Consumer component has a required port (receptable) for fetching products from producers and an event sink for getting notified when a new
product is finished.
CIDL does not only allow the definition of interfaces. It also allows expressing
component compositions, persistence issues, and implementation-related aspects
Component implementation. CCM uses CORBA as an implementation platform
and provides the so-called Component Implementation Framework (CIF) for
the construction of component implementations. The CIF provides a compiler for
generating programming skeletons from CIDL descriptions. It automates many of
the basic functions of components, such as navigation between offered ports,
identity inquiries, activation, state and life cycle management.
To simplify deployment, CCM provides standardized techniques for component
packaging and deployment. It uses package descriptors in an XML-based language to describe components and their dependencies.
At runtime, component instances are managed by a two-level mechanism. Component instances are created within so-called containers, which are part of a component server. The component server creates and manages the containers. Each
of the containers manages one component implementation as defined by the CIF.
In particular, a container creates and manages its own POA. The container also
provides access to the underlying ORB and to the CORBA Object Services.

5.5
Problem 5.1:
Stubs and skeletons

Problems for Chapter 5

Problem 5.1:
What are stubs and skeletons? What is their function in an RMI scenario? Why is
it necessary to marshal method arguments in an RMI scenario?

Problem 5.2:
How a process can
get access to a
remote reference

Problem 5.2:

Problem 5.3:
How components
written in different
languages can be
composed

Problem 5.3:

Describe two possibilities of how a process can get access to a remote reference in
RMI. What additional possibilities exist in CORBA?

Explain two different approaches of how components written in different languages can be composed. What approach uses IDLs and why?

125

Chapter 5 Distribution and Heterogeneity in Component Frameworks

Problem 5.4:
Explain the role of object adapters in scenarios where remote references (IORs)
refer to objects in non-existing server processes. Why are such scenarios supported by CORBA?

Problem 5.5:
What is the role of the Interface Repository (IFR) for CORBA's reflection capabilities?

Problem 5.6:
Why can the CORBA trading service not be used for component selection? What
additional features would be needed?

Problem 5.4:
Role of object
adapters

Problem 5.5:
Role of the
Interface
Repository

Problem 5.6:
CORBA trading
service for
component
selection

126

Chapter 5 Distribution and Heterogeneity in Component Frameworks

Chapter 6 Component Frameworks for Application Servers

Component Frameworks for Application Serv


ers

The business logic and database access in three-tier software architectures is handled by so-called business applications. The development of such applications is
very often supported by component frameworks that are specifically designed for
this task. The business-logic code is packaged into deployable components and
deployed into containers. The intention is that developers can focus their efforts
on the business-logic without having to worry about the supporting infrastructure.
The infrastructure is realized by containers that are part of so-called application
servers. The reasons for providing such architecture-specific component frameworks are:
the well-understood structure of the architecture,
common infrastructure and services of the application server,
the large economical importance of such architectures.
Currently, application development for three-tier software architectures is probably the most important area of component frameworks. For the future, we envisage that mature component frameworks and technologies will be used in many
other application areas. The central concepts explained here, in particular different
component kinds, architectural framework support, and dependency injection are
also relevant in other application domains. However, as currently no widely accepted component frameworks for embedded systems supporting these concepts
are available, we explain them together with application server development and
Enterprise Java Beans.
This chapter is structured as follows:
1. It introduces the basic concepts underlying application servers (Section 6.2).
2. It describes how the Enterprise Java Beans component framework supports the
development of deployable applications (Section 6.3).
3. It explains dependency injection and its role in component frameworks. In
particular, we show how dependency injection is supported by the EJB
framework (Section 6.4).

6.1

Learning objectives of this chapter

After studying this chapter, the reader should


know what application-specific component frameworks are,

127

128

Chapter 6 Component Frameworks for Application Servers

have understood what application servers are and why their development is an
appropriate area for component frameworks,
be able to develop and deploy simple Enterprise Java Beans,
understand the role of deployment descriptors,
have seen how annotations can simplify component development and deployment in the Enterprise Java Beans framework,
have learned what dependency injection means.

6.2

Basic concepts of application servers

There are thousands of application scenarios adhering to the following pattern: An


organization (company, airline, university, etc.) manages some data (orders, bookings, students, etc.) and offers services based on these data to remote clients
(make new orders, book a flight, enroll for the next semester, etc). Systems to
support such scenarios are typically built in three tiers:
Data tier: The data tier is responsible for the persistent storage of data and
provides the needed retrieval functionality. It is typically implemented by one
or several databases. It is important to understand that access to the databases
has to be synchronized among different clients.
Logic tier: The logic tier implements the business logic and business processes. More technically, it accepts and maintains the connections to clients,
handles authorization and security mechanisms, controls the client sessions,
and realizes the functionality of the services. The logic tier is typically structured into applications that implement the provided services.
Presentation tier: The presentation tier provides the interface to the clients.
Nowadays, it is often realized as a Web interface.
Client-server systems based on this three-tier architectural pattern will be called 3tier client-server systems in the following. They have many aspects in common.
For example, they should support:
Remote access by clients
Authentication and security
Session management, including life cycle management of component and application instances
Concurrency control
Mechanisms for linking service implementations to the underlying databases

Chapter 6 Component Frameworks for Application Servers

Transaction mechanisms for business processes that potentially span several


databases
Dynamic system management, such as load balancing, to react to varying
numbers of client requests
The basic idea of application servers is to support all these features in an application-independent way. The application developer can (re-)use these features
through APIs. In component frameworks for 3-tier client-server systems, applications are built from components and are deployed into containers. The deployment
supports the linking to the underlying databases and the connection to the security
infrastructure. Containers run as part of application servers that provide the system-level services.
A number of component frameworks for different programming languages and
platforms exist. In the next section, we take a closer look at the Enterprise Java
Beans framework. An alternative is the open-source Spring Framework, which is
available for the Java and the .NET platforms (see, e.g., [26] and [41]).

6.3

Enterprise JavaBeans

The Enterprise JavaBeans framework supports the development of componentbased, distributed, transactional business applications and their deployment within
application servers. Components within the framework are called Enterprise
JavaBeans or simply EJBs or beans. The EJB framework specification was created under the Java Community Process to provide public participation in the
definition and development. At the time of writing this text, the current version of
the specification was Version 3.0. Several realizations of the specification are
available. We used the open source GlassFish application server provided by Sun
Microsystems.
This section gives an overview of the EJB architecture and describes by a simple
example how beans look like, how they are deployed, how they can be used by
clients, and how applications with several beans can be built. Furthermore, we
describe the annotation technique available since EJB 3.0 that allows to simplify
the implementation of beans.

6.3.1

Architectural overview of EJB

In the following, we will introduce the EJB component model and the overall architecture of EJB-based systems. Finally, we will briefly mention important EJBrelated aspects that are not covered otherwise in this book.
EJB component model. Enterprise JavaBeans are the components within the EJB
framework, i.e., beans are the units of composition. To be precise, one has to distinguish between the objects at runtime and the software implementing their func-

129

130

Chapter 6 Component Frameworks for Application Servers

tionality. We speak of bean instances to explicitly refer to the runtime objects


and of bean components5 to refer to their implementation. Similar to components
in the OSGi framework, a bean instance might consist of more than one Java object and a bean component might comprise more than one Java class. Whenever
the distinction between instance and component is not necessary or clear from the
context, we simply speak of beans.
A deployable application consists of one or several beans and is constructed by
the so-called application assembler, which weaves framework classes and application-specific code together. Deployment and assembly are described by the deployment descriptor in the form of an XML document. The constructed application is deployed in an EJB container. After deployment, it is available to clients.
The application and its beans can use the mechanisms and services of the container and the surrounding application server (see Figure 6.1).

Figure 6.1:
The schematic
architecture of an
EJB-based system

There are four different kinds of beans, reflecting the different conceptual roles
and technical features of beans within a three-tier architecture:
Entity beans
Stateful session beans
Stateless session beans
Message-driven beans

5 We call it a bean component and not a bean class, because there is exactly one Java class representing the
bean, and this class is usually called the bean class.

Chapter 6 Component Frameworks for Application Servers

Distinguishing between different component kinds is a typical aspect of architecture-specific component frameworks and reflects architectural knowledge. In the
following, we explain the beans characteristics.
Entity beans essentially represent the data objects handled by the system. Such
data objects describe, e.g., bank accounts, products, assurance policies, orders,
students, or employees. As suggested by the name, an entity bean instance usually
corresponds to what is called an entity in the literature about entity-relationship
modeling and database systems. Entity bean instances are persistent, i.e., their
state is stored in a database. Entity beans link the logic tier to the data tier.
The bean developer can choose between bean-managed persistence and container-managed persistence. In the first case, she has to implement the persistence mechanisms herself; in the latter case, this is done automatically by the container. An entity bean instance can be accessed by different clients communicating
with the application server. The instance is logically identified by a so-called
primary key. The primary key is used to access existing entity bean instances.
The notion of a primary key corresponds to its meaning in the database literature
(cf. [40]).
Session beans are used to model tasks, processes, or behavior of the application
server, e.g., the task of handling a certain purchase order. A session bean corresponds to a service for a client. In particular, a session bean usually accesses several entity beans. Session bean instances are conceptually not shared between clients, i.e., a session bean instance is a private resource of a client session. However, the implementation can use sharing as long as it does not affect the system's
behavior.
For efficiency reasons, the framework distinguishes between stateless and stateful
beans. Stateful session bean instances have a so-called conversational state,
which represents the continuing conversation between the bean and the client.
This state is maintained between method invocations, but it is not persistent and is
discarded at the end of the session. Stateless session beans do not maintain their
state from one method invocation to the next. They are essentially collections of
methods.
Message-driven beans support the asynchronous communication between clients
and applications. Their realization is based on the Java Message Service (JMS).
We do not explain them here, but want to stress that their existence shows the importance of asynchronous communication for certain scenarios.
Architecture. The overall architecture of a system built with the help of EJB
technology is shown in Figure 6.1. The server side consists of an EJB container
and a number of services provided by the application server. The EJB container
contains and manages the bean components and instances. We show here session
and entity beans. Beans are created via the home interface and used via the remote
interface (details regarding these interfaces will be explained in the following sub-

131

132

Chapter 6 Component Frameworks for Application Servers

sections). Beans can be accessed directly by clients and can access each other in a
controlled way. Furthermore, they can access a naming service via the Java Naming and Directory Interface (JNDI), transaction monitors via the Java Transaction API (JTA), databases via the Java Database Connectivity API (JDBC), and
other services via the EJB and Java APIs. Most of the services are also accessible
by clients.
Further aspects. In accordance with the topic of this book, the presentation of
EJB focuses on its component-oriented concepts. However, we do not want to
conclude this subsection without mentioning that the EJB framework is part of the
Java 2 Enterprise Edition (J2EE) and that J2EE provides many other features for
the development of 3-tier client-server systems. In particular, J2EE offers an
elaborated technology for realizing the server-side aspects of Web-based applications (Java Server Pages, Servlets).

6.3.2

What beans look like

To illustrate bean development, we start with a tiny example of an entity bean. As


entity beans can be directly used by clients and also link to the data tier, they allow demonstrating both aspects without having to consider a larger scenario. However, in typical scenarios and applications, the client communication would be
handled by session beans and only they would access the entity beans (this is illustrated in Subsection 6.3.5).
A bean component consists of three or four program parts and an XML document
describing the deployment-related information:
the remote interface
the home interface
the primary key class (optional and only for entity beans)
the bean class
the deployment descriptor
We will explain these parts in turn using an entity bean representing a bank account. We place the program text of the bean into a package
ejb.samples.accounts. To use the EJB framework, one has to import the
package javax.ejb, which contains the basic interfaces and classes of the EJB
framework.
Remote interface. The remote interface defines the methods that the entity bean
provides to the client. It reflects the functionality of the bean. The bank account
bean of our example allows clients to ask for the number of the account, to query
for the balance, and to change the balance by a positive or negative amount:

Chapter 6 Component Frameworks for Application Servers

package ejb.samples.accounts;
import javax.ejb.*;
import java.rmi.*;
public interface BankAccount extends EJBObject {
String getAccountNumber()
throws RemoteException;
double getAccountBalance() throws RemoteException;
void
changeBalance(double amount)
throws RemoteException;
}

EJBObject is defined in the package javax.ejb and is a supertype of bean instances


that should be remotely accessible. It provides the following methods:
public interface EJBObject extends java.rmi.Remote {
EJBHome getEJBHome();
Handle getHandle();
Object getPrimaryKey();
boolean isIdentical(EJBObject obj);
void remove();
}

In order to support remote method invocation, EJBObject is a subtype of the type


Remote. Newer versions of EJB also support local beans (we describe local beans
below). Using the methods of EJBObject, a user can get the home object that created this bean instance (see next paragraph), get a Handle object, get the primary
key of this bean, check whether two references refer to the same bean, and remove
this bean.
Home interface and primary key. The home interface defines the methods concerning the life cycle of the bean. The client uses this interface to create bean instances; to find them, i.e., to access existing instances that were stored in the database earlier;, and to remove them. There are a several rules concerning the method
names and their return types. For example, the name of create methods have to
start with the prefix create and their return type has to be the type of the corresponding remote interface (i.e., BankAccount in our example). Home interfaces
have to extend interface EJBHome, which contains a method for deleting a bean
and, as a subtype of Remote, the functionality for remote method invocation. In
our example, the home interface defines two methods for creating and finding an
account (for the sake of brevity, we omit the package and import declarations;
they are the same as for the BankAccount interface above):
public interface BankAccountHome extends EJBHome {
BankAccount create(String accNo, double initBal)
throws CreateException, RemoteException;

133

134

Chapter 6 Component Frameworks for Application Servers

BankAccount findByPrimaryKey(String accPK)


throws FinderException, RemoteException;
}

The method findByPrimaryKey plays a special role. It has to be present in home


interfaces of entity beans. Session beans do not have this method. The method
takes a primary key as a parameter and returns the entity identified by this key. In
particular, its return type has to be the type of the corresponding remote interface.
Primary keys can be of any serializable reference type. In the example, we use
String as the primary key type. The primary key identifies an entity bean instance
in order to realize persistence. In particular, primary key objects can be stored by
clients and used to access the same bean in another session.
public abstract class BankAccountBean implements EntityBean
{
public String accountNumber;
public double accountBalance;
private EntityContext theContext;
// implements part of create method of home interface
// result type has to be primary key type
public String ejbCreate(String accNo, double initBal)
throws CreateException, RemoteException {
setAccountNumber( accNo );
// important to use
setAccountBalance( initBal );
// setter methods here!
return null; // the primary key is returned by the code
// wrapping the bean class (see 6.3.3)
}
// methods implementing the remote interface
public abstract String getAccountNumber()
throws RemoteException;
public abstract double getAccountBalance()
throws RemoteException;
public void changeBalance(double amount)
throws RemoteException {
setAccountBalance( getAccountBalance() + amount );
}
// setter methods
abstract void setAccountNumber(String accNo)
throws RemoteException;
abstract void setAccountBalance(double accBal)
throws RemoteException;
// the methods of interface javax.ejb.EntityBean

135

Chapter 6 Component Frameworks for Application Servers

public void setEntityContext(EntityContext ctx)


throws RemoteException {
theContext = ctx;
}
public void unsetEntityContext() throws RemoteException {
theContext = null;
}
public void ejbPostCreate(String accNo, double initBal)
throws RemoteException {}
public void ejbRemove()
throws RemoveException,
RemoteException {}
public void ejbActivate()
throws RemoteException {}
public void ejbPassivate()
throws RemoteException {}
public void ejbLoad()
throws RemoteException {}
public void ejbStore()
throws RemoteException {}
}

Bean class. The bean class provides implementations for the methods of the home
and remote interface. The only exception is the method findByPrimaryKey,
which is provided in the deployment process by the framework. The reader should
notice that this can be realized because the method findByPrimaryKey has a fixed
signature. This is one example illustrating how tightly naming and signature conventions, context rules, and interfaces play together in the EJB framework.
The bean class must not be declared as a subtype of the home and remote interfaces, i.e., it does not implement the interfaces in the sense of Java. However, it
has to provide implementations for their methods, i.e., method definitions with the
same signature as in the interfaces. The connection between the bean class and the
interfaces is established during deployment (see below).
Figure 6.2 presents a possible definition of the bean class for our example. The
attributes of the entity are fields in the entity object. The class declares getter and
setter methods for the fields and the remaining methods of the remote interface
(changeBalance in the example). It is important to use only the getter and setter
methods to access the fields of the bean. Furthermore, the class BankAccountBean illustrates that entity bean classes have to implement the interface EntityBean of the EJB framework. The methods of this interface manage the communication between the bean and the container. The method setEntityContext, for example, allows the container to store the context information of the framework in
the bean. This context information is used by the container to manage the identity
of bean instances.

Figure 6.2:
beanclass

136

Chapter 6 Component Frameworks for Application Servers

6.3.3

How beans are deployed

In this section, we explain how deployment descriptors are structured and how
deployment is realized.
Deployment descriptors. The information needed to configure and deploy beans
is given by deployment descriptors. A deployment descriptor especially describes
the different parts of a bean and its runtime properties, for example whether persistence should be managed by the container or by the bean itself. It allows combining several beans into one unit, describes external dependency (e.g., to databases or external beans), and provides deployment information (e.g., access rights
specifying who has the permission to invoke a method).
EJB deployment descriptors are defined in an XML format. For the purposes of
this book, it is not important to understand the details. However, the function and
general format of such descriptions are highly relevant, as they illustrate how
component frameworks can use declarative description languages with formal
syntax to capture architectural relations and properties. Such descriptions can be
used later to deploy the software systems in a semi-automatic way.
To illustrate deployment descriptors, we consider a descriptor for the BankAccount bean. The overall structure is as follows:
<?xml version="1.0" ?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.
//DTD Enterprise JavaBeans 1.2
//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<description>
deployment descriptor of entity bean BankAccount
</description>
<enterprise-beans>
... <!-- see extra figure and text -->
</enterprise-beans>
<assembly-descriptor>
... <!-- see text -->
</assembly-descriptor>
</ejb-jar>

The first four lines declare the XML version and the document type used. The
content enclosed in ejb-jar tags consists of an optional description, the list of enterprise beans belonging to the deployment unit (at least one bean has to be
given), and other optional parts. Above, we have illustrated the other optional
parts with an assembly-descriptor element. Assembly descriptors can define security roles used to access beans, can declare method permissions (which roles can
call which method), and can provide information about transactional behavior.

Chapter 6 Component Frameworks for Application Servers

<enterprise-beans>
<entity>
<description>
BankAccount represents a bank account
</description>
<!-- name of the bean in JNDI -->
<ejb-name>BankAccount</ejb-name>
<!-- fully qualified name of home interface -->
<home>ejb.samples.accounts.BankAccountHome</home>
<!-- fully qualified name of remote interface -->
<remote>ejb.samples.accounts.BankAccount</remote>
<!-- name of bean class -->
<ejb-class>ejb.samples.accounts.BankAccountBean</ejb-class>
<!-- bean uses container-managed persistence -->
<persistence-type>Container</persistence-type>
<!-- class of primary key -->
<prim-key-class>java.lang.String</prim-key-class>
<!-- bean is not reentrant -->
<reentrant>False</reentrant>
<!-- fields of bean that should be persistent -->
<cmp-field>
<field-name>accountNumber</field-name>
</cmp-field>
<cmp-field>
<field-name>accountBalance</field-name>
</cmp-field>
<!-- field in bean corresponding to primary key -->
<primkey-field>accountNumber</primkey-field>
</entity>
</enterprise-beans>

Figure 6.3 shows the entity part of the deployment descriptor for the BankAccount bean. The comments (enclosed in <!-- ... -->) describe the different elements
of the descriptor. The deployment descriptor for an entity bean declares in particular which fields of the bean should be made persistent.

137

Figure 6.3:
entityPartDeploym
ent

138

Chapter 6 Component Frameworks for Application Servers

Figure 6.4:
Generation of EJB
home and EJB
remote class

Deployment mechanism. According to the Enterprise JavaBeans specification,


the developers/vendors of EJB servers have to provide tools for the deployment of
beans. To deploy a bean X, the tools take the parts of X as input and generate two
classes: a so-called EJB home class for X implementing the home interface and a
so-called EJB object class for X implementing the remote interface (see Figure
6.4). The names of these two classes are not standardized and hidden for deployers. Objects of the first class are often called EJB home objects; those of the second class are often called EJB remote objects. It is interesting to see here the use
of generation techniques as part of the deployment mechanism.

Figure 6.5:
Bean access via
EJB home and EJB
remote object

Chapter 6 Component Frameworks for Application Servers

The EJB home and EJB remote objects encapsulate the bean instance. A client can
only access a bean by invoking methods of the home and remote objects. These
objects delegate the client's method invocations to the bean instance (see Figure
6.5). In addition, the EJB home and EJB remote objects perform operations realizing important tasks for the container, in particular resource management, security
mechanisms, transactions handling, and persistence management. How these operations are implemented depends on the server developer. It is important that the
EJB framework supports such a delegation and encapsulation mechanism:
In this way, the container has control over bean instances and can prevent incorrect or malicious access to beans.
It is the basis for relating the component infrastructure to the components.
It largely frees the bean developers from infrastructure issues.
The use of EJB home and remote classes also explains why the bean class must
not be a subtype of the home and remote interfaces, i.e., why the bean class does
not implement the interfaces in the sense of Java. The implementation of these
interfaces is provided by the EJB home and EJB remote classes. However, in order to enable delegation, the method signatures of the home and remote interfaces
have to match those of the bean class.

6.3.4

How beans are used by clients

To illustrate the usage of beans, we consider a client who accesses an instance of


the BankAccount bean described above. She has to know the name by which the
bean is made accessible in the container. Usually, this is the name of the bean as
given in the deployment descriptor and a prefix depending on JNDI conventions
and the deployment. In our example, the bean name is BankAccount (see Figure
6.3), the JNDI schema is java:global, and the name of the deployment unit is
bankunit; thus, the full name is java:global/bankunit/ BankAccount. The following program fragment shows the essential steps needed to access and use a
bean:
InitialContext ctx = new InitialContext();
Object o = ctx.lookup("java:global/bankunit/BankAccount");
BankAccountHome bh = (BankAccountHome)
PortableRemoteObject.narrow(o,BankAccountHome.class);
BankAccount ba = bh.create("23456735",0.0);
ba.changeBalance(32.00);

The initial context is needed for the naming service. After lookup, the client has to
narrow the resulting reference to assign it to a variable of the type BankAccountHome. Performing the narrowing with a method and not with a simple cast enables the EJB framework to perform additional operations at that point. The
BankAccountHome object is then used to create a new empty bank account with

139

140

Chapter 6 Component Frameworks for Application Servers

the account number "23456735". This leads to the creation of a bank account instance, an entity in the underlying database, and a primary key object that will be
associated with the bean instance. Now, the client can make use of the bean's
functionality. To illustrate this, the last line shows an invocation of the method
changeBalance.
In summary, we can see that the use of beans is similar to the use of remote objects. The management of beans by the container is hidden from the client.

6.3.5

How beans are used together

In this section, we demonstrate session beans and server-local beans and show
how they work together. Local beans can be accessed directly by other local beans
without the overhead caused by remote method invocation. In the next section, we
will repeat this example in a version using the Java annotations available since
EJB 3.0 to illustrate the simplifications that are possible with this annotation
mechanism.
As a running example, we use a banking bean with the following interface:
public interface Banking extends EJBObject {
void transferMoney(String fromAccNo,
String toAccNo, double amount)
throws RemoteException;
double getAccountBalance(String accNo)
throws RemoteException;
}

The Banking bean is a session bean that allows transferring money from one account to another account and looking up up the balance of an account. As a session bean, the home interface only needs a create method:
public interface BankingHome extends EJBHome {
public Banking create()
throws CreateException, RemoteException;
}

public class BankingBean implements SessionBean {


private SessionContext theContext;
public void ejbCreate()
throws CreateException, RemoteException {}
public void transferMoney(String fromAccNo,
String toAccNo, double amount){
BankAccount fromAcc = findByAccountNumber(fromAccNo);
BankAccount toAcc
= findByAccountNumber(toAccNo);

141

Chapter 6 Component Frameworks for Application Servers

fromAcc.changeBalance( -amount );
toAcc.changeBalance( amount );
}
public double getAccountBalance(String accNo) {
return findByAccountNumber(accNo).getAccountBalance();
}
private BankAccount findByAccountNumber(String accNo) {
try {
InitialContext ictx = new InitialContext();
Object o =
ictx.lookup("java:comp/env/LocalBankAccount");
BankAccountHome bah = (BankAccountHome) o;
return bah.findByPrimaryKey(accNo);
} catch (Exception e) {
... // Exception handling
return null;
}
}
// the methods of interface javax.ejb.SessionBean
// are omitted here; they are similar to Sect. 6.3.2
}

The banking bean class is shown in Figure 6.6. As a session bean, it implements
the interface SessionBean. The developer-defined method findByAccountNumber
shows how other beans can be accessed. Similar to the remote case, the connection to bank account beans is established via the JNDI lookup mechanism and the
primary key. As it is a local lookup, the JNDI prefix is java:comp/dev. The
name of the local bean class is defined in the deployment descriptor, which in this
case is LocalBankAccount (cf. Figure 6.7). Furthermore, it should be noted that
the returned bank account home object is only cast to the corresponding type. A
narrow operation is not necessary here, because the returned reference is a reference to a local object.
The local implementation of the bank account bean (not shown) is almost the
same as the remote one discussed in the subsections above. The only difference is
that the remote interface is now a local interface extending EJBLocalObject
and the home interface extends EJBLocalHome. Furthermore, the methods do
not throw remote exceptions.
<enterprise-beans>
<session>
<ejb-name>Banking</ejb-name>
<home>ejb.samples.accounts.BankingHome</home>
<remote>ejb.samples.accounts.Banking</remote>

Figure 6.6:
BankingBean

142

Figure 6.7:
deploymentDescrip
torBankingBean

Chapter 6 Component Frameworks for Application Servers

<ejb-class>
ejb.samples.accounts.BankingBean
</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<ejb-local-ref>
<ejb-ref-name>LocalBankAccount</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>
ejb.samples.accounts.BankAccountHome
</local-home>
<local>ejb.samples.accounts.BankAccount</local>
<ejb-link>BankAccount</ejb-link>
</ejb-local-ref>
</session>
<entity>
<description>
BankAccount represents a bank account
</description>
<ejb-name>BankAccount</ejb-name>
<!-- fully qualified name of home interface -->
<local-home>
ejb.samples.accounts.BankAccountHome
</local-home>
<!-- fully qualified name of local interface -->
<local>ejb.samples.accounts.BankAccount</local>
<ejb-class>
ejb.samples.accounts.BankAccountBean
</ejb-class>
<!-- other elements are as descriptor in 6.3.3 -->
</entity>
</enterprise-beans>
}

The main parts of the deployment descriptor for the example are shown in Figure
6.7. The deployment descriptor contains information about both beans and shows
how session beans and local beans are treated in the XML format. The most interesting part is the ejb-local-ref element of the session bean description. It declares
the name under which the local bean is accessible and the names of its classes.
Finally, we illustrate the use of banking beans by clients. The steps are the same
as those explained in Subsection 6.3.4:
InitialContext ctx = new InitialContext();
Object o = ctx.lookup("java:global/bankunit/Banking");
BankingHome bah = (BankingHome)

Chapter 6 Component Frameworks for Application Servers

PortableRemoteObject.narrow(o,BankingHome.class);
Banking ba = bah.create();
ba.transferMoney("12345", "897979", 500.0);

6.3.6

Simplifying bean development: EJB 3.0 annotations

Even with the simple banking example from the last section, it becomes quite
clear that the explicit declaration of remote and home interfaces as well as the
formulation of the deployment descriptor can become a burden for programming
and program maintenance. That is why EJB 3 provides a mechanism for simplifying bean development by introducing annotations. With annotations,
home interfaces are no longer needed for remotely accessible beans
local and home interfaces are no longer needed for local beans,
beans access is simplified,
deployment descriptors can be simplified or avoided,
dependency injection is supported (see Section 6.4).
In this section, we demonstrate the mechanism by rewriting the example of Subsection 6.3.5 with annotations.
The banking bean gets the following interface with the annotation @Remote:
@Remote
public interface Banking {
void transferMoney(String fromAccNo,
String toAccNo, double amount);
double getAccountBalance(String accNo);
}

It should be noted that this version of the interface Banking does not extend
EJBObject. Furthermore, the declaration of remote exceptions is no longer necessary. A home interface for Banking need not be defined.
@Stateless
public class BankingBean implements Banking {
@PersistenceContext(unitName="bankcontext")
private EntityManager em;
public void transferMoney(String fromAccNo,
String toAccNo, double amount){
BankAccountBean fromAcc= findByAccountNumber(fromAccNo);
BankAccountBean toAcc = findByAccountNumber(toAccNo);

143

144

Chapter 6 Component Frameworks for Application Servers

fromAcc.changeBalance( -amount );
toAcc.changeBalance(amount);
}
public double getAccountBalance(String accNo) {
return findByAccountNumber(accNo).getAccountBalance();
}
private BankAccountBean findByAccountNumber(String accNo){
return (BankAccountBean)
em.find(BankAccountBean.class, accNo);
}

Figure 6.8:
BankingBeanV3

As shown in Figure 6.8, the bean class for Banking is declared to be a stateless
session bean implementing the interface Banking. That is, it no longer implements
the interface SessionBean. Compared to the version in Figure 6.6, it is simplified
in two ways:
The methods of the interface SessionBean need not be implemented. The
connection to the entity context is handled by dependency injection (see Section 6.4) based on the annotaion:
@PersistenceContext(unitName="bankcontext")

The method find of the entity manager provides more direct access to the bank
accounts.
Whereas a deployment descriptor is no longer necessary, a small XML document
is needed to describe the persistence context. This document refers to the name
"bankcontext" in the persistent context annotation and describes the connection to
the underlying database or data layer.
@Entity
public class BankAccountBean implements Serializable {
private String accountNumber;
private double accountBalance;
public BankAccountBean() {}
public BankAccountBean(String accNo, double initBal) {
setAccountNumber(accNo);
setAccountBalance(initBal);
}
@Id
public String getAccountNumber() {
return accountNumber;

Chapter 6 Component Frameworks for Application Servers

}
private void setAccountNumber(String accNo) {
accountNumber = accNo;
}
public double getAccountBalance() {
return accountBalance;
}
private void setAccountBalance(double accBal) {
accountBalance = accBal;
}
public void changeBalance(double amount) {
setAccountBalance( getAccountBalance()+amount );
}
// no methods missing !!
}

Using the annotations, the realization of the entity bean BankAccount only consists of the one class shown in Figure 6.9. In this version, the bean class is not a
subtype of the interface EntityBean, so that the implementation of the inherited
method signatures is avoided. The annotation @Id marks the getter method returning the primary key.
Finally, the use of beans is simplified because the new version avoids the detour
via the home object:
InitialContext ctx = new InitialContext();
Object o = ctx.lookup(Banking.class.getName());
Banking ba = (Banking) o;
ba.transferMoney("12345", "897979", 500.0);

In Section 6.4, we will explain further simplifications and mechanisms based on


annotations.

6.3.7

Further aspects of EJB

EJB is a programming language dependent framework based on Java and on XML


deployment descriptors. In the following, we summarize and discuss important
aspects of the EJB framework on a more conceptual level.
Conventions and rules. The EJB specification contains a fairly large number of
conventions and rules that have to be observed in the development of components
and their parts. These conventions and rules make it possible for the parts to work
together seamlessly. More important, they allow for communication with the underlying container and, to a large extent, make the beans independent of the special implementation of the container.

145

Figure 6.9:
BankAccountBean
V3

146

Chapter 6 Component Frameworks for Application Servers

Some of the rules and conventions have already been explained along with the
above description. To get a broader view, we provide a few more rules. The
reader is encouraged to figure out why such restrictions are imposed on the components:
A bean must not return the implicit parameter this as the result of a method
or pass it as a parameter.
Static variables are forbidden in bean classes.
Use of threads and synchronization mechanisms is not allowed.
Use of GUI facilities as well as input- and output-operations are forbidden
(this includes socket communication).
Use of introspection and reflection must be avoided.
Beans should not influence class loading and security management.
The basic reason underlying these restrictions is to create a well-defined interface
between the components and the container, or in more general terms, between the
components and their infrastructure. This interface has to take into account behavioral and security aspects. For example, deadlocks have to be avoided, and the access rights of beans have to be restricted so that they cannot manipulate the data
of other beans.
Managing beans. In addition to controlling the life cycle of beans, the container
is responsible for the efficient management of bean instances. As a container has
to deal with many clients simultaneously, it cannot keep all instances in its main
memory. That is why many containers provide mechanisms for passivating and
reactivating instances by temporarily storing them on disk. To reduce the number
of needed instances, containers share instances or keep instances of the same type
in pools. Pooling of instances allows reuse of an instance, thereby avoiding the
situation that an instance is removed and afterwards, an equal instance is created
again. Another technique for increasing performance is support for load balancing between different containers: If one container is very busy, use another less
frequented container. Of course, load balancing has to be realized in such a way
that it is transparent to clients.
Persistence. According to the Enterprise JavaBeans specification, EJB containers
have to support a mechanism that is called container-managed persistence. An entity bean with container-managed persistence relies on the container to perform
persistent data access on behalf of the bean instances. The container has to automatically transfer the data between the bean instance and the underlying database.
In particular, it implements the creation, removal, and lookup of the entity object
in the database. Furthermore, it manages the mapping between primary key and
EJBObject.

Chapter 6 Component Frameworks for Application Servers

Transactions. Transactions free the application programmer from dealing with


the complex issues of failure recovery and multi-user programming in the context
of maintaining data consistency. The EJB framework supports distributed transactions that allow application scenarios where an application automatically updates
data in multiple databases that may be distributed across multiple sites. The application programmer can choose between bean- or container-managed transaction
demarcation. In the latter case, the container demarcates transactions according to
instructions provided by the application assembler in the deployment descriptor.
These instructions describe, e.g., whether the container should run a method in a
client's transaction, in no transaction, or in a new transaction that the container has
to start. In order to support such mechanisms, the EJB framework comprises a
transaction model.
Security. The EJB framework provides several mechanisms for lessening the
burden of the bean developer in terms of securing the applications assembled from
the beans. The container provides the implementation of a security infrastructure
that can be controlled by so-called security policies. It is beyond the scope of this
book to go into detail about these mechanisms. What is important here is the clear
separation between application code, i.e., beans, and security code, i.e. the security infrastructure of the container and the security policies.

6.4

Dependency injection and bean composition

A component C may depend on services provided by other components or by the


container. These services constitute the required interface or interfaces of C (see
Chapter 2). After creation of an instance of C, the new instance has to be supplied
with the required services. This can be done:
at initialization time, i.e., directly after creation and before registration and
startup of the component instance;
at runtime, i.e., some later time in the lifetime of the instance.
We call services of the former case initially required services, those of the latter
case dynamically required services. Examples of initially required services are
illustrated by the ThermoControl scenario in Chapter 2, the PhoneNumber application in Chapter 4, and the Agent component in Subsection 4.3.2. Examples of
dynamically required services are the more dynamic Agent component in Subsection 4.3.3, which updates its vendor set whenever a new vendor registers itself,
and the banking session bean in Subsection 6.3.5, which dynamically connects to
the entity bean for the given account number. Whereas the details of dynamically
required services are usually specific to the application and need special treatment
by the component developer, initially required services can often be automatically
supplied by the component framework. In this section, we explain such a mechanism, the so-called automated dependency injection (Subsection 6.4.1), and demonstrate it with an EJB example (Subsection 6.4.2).

147

148

Chapter 6 Component Frameworks for Application Servers

6.4.1

Inversion of control and dependency injection

To explain inversion of control and dependency injection, we consider a component C and an initially required service S of C. We distinguish four patterns of
how C can be connected to S:
1. The component code of C creates an instance of a component D that implements S. Then, C connects itself to S.
2. The component code of C looks up S in the context and connects to it.
3. The component C comes together with an activator code that creates and initializes instances of C. The activator looks up the service in the context and
passes it to the component instance (the TCF headers, the OSGi activator
classes, and the EJB home classes are examples of such activator code).
4. The component container, i.e., the component-independent code, provides the
instances of C with the service S.
In the first pattern, C has full control over which component it creates to get service S. In the other patterns, it is the context and the container that control which
implementation of S is used. This form of inversion of control6 allows component frameworks to automate parts of the instantiation and connection process of
components under certain conditions.
The second and third pattern above use so-called service locators in the context
of a component to look up a service. In component frameworks, service locators
are usually provided by the framework. In OSGi, for example, the method getServiceReference of the class BundleContext acts as service locator (see Chapter 4).
Using service locators has the advantage that component code is decoupled from
the code that provides the service. However, it is still the component code or the
activator that has to perform the lookup and set the service reference. If an application consists of many components and the components have many dependencies, a lot of code has to be written for making all the connections. Automated dependency injection aims to avoid such coding and to establish the connections
automatically based on declarative dependency definitions.
Dependency injection means establishing the connection of a component C to its
(initially) required services from the outside of C. In a component framework, it is
usually the container that injects the service references. Three forms of injection
are used:

6 There are other forms of inversion of control; for example in the area of graphical user interfaces, inversion of control means that the user interface takes over control from the application
code.

Chapter 6 Component Frameworks for Application Servers

1. Constructor injection: The required services are passed as arguments of the


component constructor (see for example the constructor Thermostat in Subsection 2.3.3). Constructor injection only works in situations with acyclic dependencies.
2. Setter injection: For every required service, there is a corresponding setter or
register method (see, for example, the method setEntityContext in Figure 6.2.
3. Interface injection: Interface injection is a generalization of setter injection.
The component implements interfaces with methods that take the services as
arguments and establish the connections.
To achieve automated dependency injection, a component framework can require components to adhere to certain conventions and can provide containers that
exploit the conventions to automatically create component instances and establish
the service connections. For example, a component framework can prescribe that
components have to declare on which services they depend. If a component instance is created, the container automatically looks up the required services and
injects them into the new instance. In case a required service is not available, the
container automatically selects and instantiates a component implementing the
services. In case a required service is implemented by several available components, a configurable policy is used to select one of them.
Mechanisms for automated dependency injection can be much more elaborate
than the simple techniques sketched above and are often supported by specifically
formated declarative descriptors (for example XML-based declarations of component dependencies). In the next subsection, we demonstrate how the EJB 3 framework supports automated dependency injection via Java annotations.

6.4.2

Dependency injection and Enterprise JavaBeans

The EJB 3 framework supports automated dependency injection via deployment


descriptors and bean annotations. The goal of this subsection is to
illustrate automated dependency injection,
give a feeling of the simplifications that can be achieved by it, and
show how this is realized in a concrete framework, namely EJB.
As an example, we use the phone number scenario of Figure 4.2, and realize the
components PhoneNumberNA, ZipCode, and PhoneNumber as stateless session
beans. To consider two different injection modes, we assume that PhoneNumberNA uses ZipCode via the service interface Address2Zipcode and that it uses
the PhoneNumber bean directly, i.e., without an intermediate service type.

149

150

Chapter 6 Component Frameworks for Application Servers

The ZipCode bean implements the interface Address2Zipcode. Address2Zipcode


provides a method that takes an address and returns the corresponding zip code:
@Local
public interface Address2Zipcode {
String getZipcode(String address);
}
@Stateless
public class ZipCodeBean implements Address2Zipcode {
public String getZipcode(String address) {
... ; return ...;
}
}

The PhoneNumber bean provides a method that takes a name and a zip code and
and returns the corresponding telephone number; as stated above, we do not use
an extra interface for this bean to demonstrate direct injection of beans (see below):
@Stateless
public class PhoneNumberBean {
public String getNumber(String name, String zipcode) {
... ; return ...;
}
}

The PhoneNumberNA bean allows remote access and provides a method that
takes a name and an address and returns the corresponding telephone number. The
remote interface is simply declared as:
@Remote
public interface PhoneNumberNA {
public String getNumber(String name, String addr);
}

The most interesting part is the bean class for this interface:
@Stateless
public class PhoneNumberNABean implements PhoneNumberNA {
@EJB
Address2ZipCode a2z;
@EJB
PhoneNumberBean nz2p;
public String getNumber(String name, String addr) {
return nz2p.getNumber(name, a2z.getZipcode(addr) );
}
}

151

Chapter 6 Component Frameworks for Application Servers

This bean has two dependencies. It depends on the service Address2Zipcode and
on the PhoneNumber bean. The dependencies are expressed by the annotation
@EJB. The EJB framework derives from this annotation that it has to inject
a service implementing Address2ZipCode into the PhoneNumberNA bean
a PhoneNumberBean into the PhoneNumberNA bean.
The first dependency is called an interface dependency, the second a class dependency. The framework especially provides the setter methods for the injection. Using the dependencies, the PhoneNumberNA bean implements the method
getNumber.
The important message is that the code above is complete. It can be deployed into
an EJB container and the container creates the needed beans and establishes the
connections automatically by dependency injection when the PhoneNumberNA
bean is used. Here is a fragment illustrating the use of the bean:
InitialContext ctx = new InitialContext();
Object o = ctx.lookup(PhoneNumberNA.class.getName());
PhoneNumberNA na2p = (PhoneNumberNA) o;
System.out.println(na2p.getNumber(
"Guido Merkel","Paul-Beliebig-Strae 42;Bonn"));

In summary, the dependency injection mechanism of the EJB framework supports


the declaration of dependencies through code annotations and handles bean creation and dependency injection automatically. To achieve automation, the mechanism uses a number of conventions. For example, interface dependencies can be
injected automatically if there is exactly one bean implementing the interface (this
was the case in the example above). Otherwise more information has to be given,
either as part of the annotation or within a deployment descriptor.

6.5

Problems for Chapter 6

Problem 6.1:

Problem 6.1:
Frameworks

What is the advantage of an architecture-specific component framework compared to an architecture-neutral framework?

Problem 6.2:
Explain the program parts of a bean and their role in the EJB framework.

Problem 6.2:
Program parts of a
bean

152

Chapter 6 Component Frameworks for Application Servers

Problem 6.3:
Delegation
mechanism

Problem 6.3:
Why does EJB use a delegation mechanism between the EJB home and remote
objects and the instances of the bean class?

Problem 6.4:
Simplifications
explained in 6.3.6

Problem 6.4:

Problem 6.5:
Restrictions on
beans

Problem 6.5:

Problem 6.6:
Dependency
injection

Problem 6.6:

How could the simplifications explained in 6.3.6 be achieved? What does this
mean more generally for architecture-specific component frameworks?

Explain why the restrictions named in the paragraph Conventions and rules are
imposed on beans.

What are the advantages and disadvantages of the three described forms of dependency injection?

Chapter 7 Components for Embedded Software

Components for Embedded Software

An embedded system is a computer system designed to perform one or several


dedicated functions, often with real-time computing constraints. It is embedded
into a complete device, often including computer hardware and mechanical parts.
Component frameworks like OSGi and frameworks for distribution and heterogeneity like CORBA are used as well for embedded software. However, for many
embedded applications and domains, these general frameworks and their infrastructure require too many resources and fail to provide needed guarantees. So far,
widely used component models and frameworks for embedded systems (like EJB
or Spring for 3-tier client-server systems) do not exist. Currently, different domain-specific and often proprietary frameworks are used. Therefore, it is difficult
to establish a component market for different vendors. The development of more
general frameworks for component-based development of complex heterogeneous embedded systems is still an ongoing research challenge.
The goal of this short chapter is to explain the special requirements for embedded
component software and to describe some component models and frameworks
discussed in the literature. These approaches are definitely less mature than the
technologies presented in the chapters above and are only used by one company
or a small number of users. They should be read as steps towards more general
component frameworks for embedded systems.

7.1

Learning objectives of this chapter

After studying this chapter, the reader should


have understood the additional requirements of CBSD in the area of embedded
software,
have an idea of the different specific features of component frameworks and
component architectures for embedded systems.

7.2

Requirements for embedded component software

The component frameworks presented in the last chapters make platform assumptions that can often not be met in embedded systems. For example, OSGi assumes
a Java Virtual Machine, most CORBA implementations use TCP/IP as the underlying communication layer, and EJB is based on application servers with large
memory requirements and a number of additional platform assumptions. These
resources are not or only partially available in smart cards, field devices, consumer electronics, mobile phones, and vehicles.
Embedded software and thus component technology for embedded software is
faced with the following special requirements:

153

154

Chapter 7 Components for Embedded Software

Diversity of the underlying platforms: Embedded systems run on very different, often dedicated kinds of hardware and use a variety of software for operating system and communication functionality.
Stricter resource limitations: For reasons of cost, size, and energy consumption, memory and computation power is kept as low as possible and has to be
used with much more care than in business applications on general-purpose
platforms.
Non-functional properties: As embedded software is often used for the control
of real-world devices (valves, car speed, aircrafts, etc.), real-time constraints
and quality-of-service properties play a crucial role.
To meet these requirements, component frameworks for embedded systems have
to support components with different execution models (synchronous, asynchronous, clock-synchronous, real-time), different communication models (synchronous vs. asynchronous) and different scheduling paradigms. Frameworks must
allow the construction of systems that use several of these models. To handle nonfunctional properties, component models must be equipped with information
about runtime and quality of service properties (see, e.g., rich components [21] or
the Palladio component model [10]).
Another aspect of component-based approaches for embedded systems is the larger development gap between the component descriptions and their deployed implementations. The gap is larger than in business application areas, because it is
more difficult to
find a component mapping to the more complex and heterogeneous platforms,
satisfy the non-functional properties, and
select appropriate scheduling and timing parameters.
To overcome these additional problems, there is a trend towards hybrid approaches combining component-based and model-based development by integrating refinement steps into the component frameworks (we will briefly discuss this
issue together with AUTOSAR below).

7.3

Frameworks for embedded software

This section sketches some approaches of CBSD for embedded systems. We selected the approaches to illustrate certain aspects of our topic. We do not claim
that the selection gives a balanced overview of the field (see, for example, [2] for
a presentation and comparison of several other interesting component approaches).

Chapter 7 Components for Embedded Software

7.3.1

Pin: A domain-unspecific component framework for


embedded systems

Pin [23]) is a basic, simple component technology for embedded software developed at the Software Engineering Institute (Pittsburgh, USA). Pin has been used
in different application domains, e.g., in substation automation and robotics. Its
design was governed by the following major objectives:
1. Simple programming model with an execution model following the semantics
of UML statecharts
2. Adaptable to the needs of new applications and platforms
3. Provision of means to enforce design and implementation constraints that are
extrinsic or later added to Pin
4. Support of the basic features for building predictable embedded software.
Pin can be considered as an extensible and adaptable prototype component
framework. It has been used to develop predication-enabled component technologies (PECTs) in different application domains.
Pin supports a non-hierarchical component model. Components can be specified
by UML statecharts. All needed APIs are available in the programming language
C. A component can receive messages at a finite number of so-called sink pins
and send messages over a finite number of so-called source pins. The behavior of
a component is defined in terms of a finite number of so-called reactions. A reaction can be considered as a thread waiting for incoming messages and producing
outgoing messages. Each reaction is responsible for handling the messages at a
subset of the sink pins.
The implementation of each component is put into a prefabricated generic container, that is, there is one container per component. Containers manage the life
cycle of components and the communication with other components, realize the
connections to the underlying runtime environment with real-time extensions, and
provide interfaces for component analysis. The connections between components,
that is, between source and sink pins, are configured statically and cannot be
modified at runtime. A construction and composition language CCL is available
to describe such assemblies. Pin supports synchronous and asynchronous message-based communication between components, but does not support distribution.
The component model defines rules for how components may interact with one
another and how they share resources. The runtime environment enforces these
rules of interaction and provides basic services for resource sharing, communication, scheduling, and further configuration aspects.

155

156

Chapter 7 Components for Embedded Software

In summary, Pin is a component framework for concurrent, non-distributed embedded systems providing a lightweight, message-based, flat component model
with efficient implementations in C and basic services for resource management.

7.3.2

PECOS: A CBSD approach to field devices

PECOS ([34]) was a project for the development of a component-based technology for so-called field devices. Field devices are small embedded hard real-time
systems for automatically controlling processes in the real world. They collect
data via sensors, e.g., for temperature, pressure or speed; process the data, and
control actuators like valves or motors to affect the real world (cf. the ThermoControl example in Chapter 1). A typical field device has a small micro-processor
with 1 MB of ROM and even less RAM. It usually communicates over dedicated
bus systems with other devices.
PECOS is an example of a domain-specific approach, that is, it aims to address
the needs in the sketched domain. It is interesting because of its specific component model. As in models underlying synchronous languages (see [12]), PECOS
components communicate over shared variables called ports. These ports can be
written by one component and read by other components, meaning that components communicate by means of information flow. Control flow is treated separately (see below).
The PECOS model is hierarchical and distinguishes between so-called leaf components, which have no internal structure, and composite components, which are
statically composed of other components. For the client of a component, the internal component structure is not visible; that is, the client cannot distinguish between leaf and composite components. Concerning the dynamic behavior, the
PECOS model supports three different component kinds:
Passive components: They do not have their own thread of control. They are
triggered from active enclosing components and are used to realize a function
that executes synchronously and completes within a "short time".
Active components: They have their own thread of control and realize longerliving activities. In particular, the complete software of a field device is usually modeled as a composite active component.
Event components: Their behavior is triggered by external events. They are
used, in particular, to model hardware components and integrate them into the
component model.
In the PECOS model, synchronization and scheduling of components is explicitly
described (that is, there is no global clock as in the clock-synchronous component
model described in Subsection 2.4.5). Every composite component has a scheduler. Petri nets are used to model the scheduling and analyze the validity of compositions.

Chapter 7 Components for Embedded Software

PECOS is interesting because of its separation of information and control flow


and because it demonstrates that component models for embedded systems have
to be able to incorporate hardware components into the model.

7.3.3

Koala: A CBSD framework for consumer electronics

Different component models have been developed for the domain of consumer
electronics (see [32] for an analysis of the specific requirements). One specific
aspect in this domain of embedded software is the high diversity of products and
the resulting need to support variants within the component model. In the following, we introduce the Koala component model ([35]) to illustrate these aspects.
Koala incorporates concepts from architecture description languages (in particular
from Darwin) and product-line engineering (cf. Subsection 3.4.2).
The software in consumer electronic (CE) products realizes a large range of functionality from basic hardware control via signal and data processing, support for
product features like electronic programming guides and graphical user interfaces,
all the way to integration with Web technology. CE products have become a
member of complex product-family structures and CBSD for such products has to
cope with this aspect.
Koala strictly separates component and configuration development. Components
have provided and required interfaces where an interface consists of a small set of
related C function signatures (the implementation language of Koala as presented
in [35] is C). Even general services and system calls like memory management
have to be handled through required interfaces. Koala supports a component description language CDL to specify the interfaces of a named component and to
construct compound components; that is, it supports a hierarchical component
model. Components can be linked directly by connecting required to provided interfaces using CDL. In addition, so-called modules can be used to glue interfaces
together. The glue code may depend on configuration parameters.
To handle diversity without losing resources at runtime, Koala combines flexible
parameterization mechanisms for components with partial evaluation techniques.
Koala components are explicitly parameterized across all their configurations. Instead of handling the parameters as component properties that have to be set from
the outside, Koala uses so-called diversity interfaces. A diversity interface is a
required interface through which a component asks the environment for the properties. Components use the diversity interfaces at configuration time to set internal
parameters. In addition to that, so-called switches are provided to handle structural diversity in compound components. A switch allows linking an interface of
one component to two or more alternative component interfaces depending on
configuration parameters.
At configuration time, the actual configuration parameters are provided from outside the component. A partial evaluation process evaluates all expressions that

157

158

Chapter 7 Components for Embedded Software

guide the configuration. Whenever sufficient information can be derived, switches


are eliminated and interfaces are connected directly. All components that are not
needed for the constructed configuration are excluded from the configuration.
In summary, Koala is an interesting example of a hierarchical component framework that supports a high degree of configuration variability without sacrificing
resource consumption or execution efficiency.

7.3.4

AUTOSAR: AUTomotive Open System ARchitecture

AUTOSAR (see www.autosar.org) is a joint initiative of a number of major players in the automotive industry to develop an AUtomotive Open System ARchitecture and to overcome the current situation, which is characterized by proprietary
solutions that hinder the exchange of components and applications among automotive OEMs and suppliers. As the name already suggests, the initiative goes beyond the development of a component framework. We consider the approach here
to illustrate the close relationship between architecture, component frameworks,
and development method in an important embedded system domain.

Figure 7.1:
Overview of the
two-layered
AUTOSAR
approach

Chapter 7 Components for Embedded Software

159

The overall AUTOSAR approach is sketched in Figure7 7.1. It distinguishes two


conceptional layers: a description layer and an implementation layer. The description layer consists of
the software component descriptions,
the descriptions of the electronic control units (ECUs), and
the description of the system constraints.
The software components (SW-C) constitute the logical functions visible to costumers. They are described irrespective of a programming language or hardware
platform. The SW-C descriptions include software interfaces, behavior and timing
information, hardware interfaces, and requirements on runtime performance. Connections and communication in this logical view are expressed via a virtual functional bus VFB.
There has to be a description of each ECU in the system specifying its type (sensor, actuator, etc.), hardware interfaces, and properties. Furthermore, system-wide
information concerning network infrastructure and component distribution has to
be given (system constraint description). This comprises the description of the real
bus systems, of protocols, of component clustering, and of deployment to ECUs.
The descriptions are used to control a tool-supported deployment mapping of
components to the ECUs of the system. A component can run on multiple ECUs
(see SW-C 2 in Figure 7.1) and several components can run on one ECU (see
ECU I). The deployment includes the configuration of the basic software and runtime environment on the ECUs. The basic software provides services needed on
the operating-system level (memory management, network management, etc.).
The runtime environment provides the VFB functionality needed on an ECU using the services of the basic software. That is, it mediates between the requirements of the AUTOSAR components and the services provided by the basic software.
In summary, AUTOSAR demonstrates the role of component models within more
comprehensive architectural-driven, domain-specific development frameworks for
the exchange of software components and applications.

7.4

Problems for Chapter 7

Problem 7.1:
For each of the frameworks presented in this chapter, name at least one feature,
aspect, or characteristic that goes beyond general component frameworks.

7 The figure is cited from the technical overview of AUTOSAR at http://www.autosar.org.

Problem 7.1:
Restrictions on
beans

160

Chapter 7 Components for Embedded Software

Chapter 8 Review and Current Developments

Review and Current Developments

This chapter briefly reviews the central conceptual aspects treated in the book and
and sketches current lines of research and development in CBSD.

8.1

Review

The goal of the book was to introduce the motivation for CBSD and to explain the
main concepts and important frameworks. Keeping away from the details and using many simplifications, our aim here is to review the story of the book.
Chapter 1 - Introduction. When complex systems are built, it is a good idea to
have a clear view of what the components are: Divide et impera! Having a clear
notion of software components that allows analyzing the components one by one
simplifies techniques for quality assurance, in particular testing and verification.
Furthermore, complete systems composed from well-understood components are
easier to maintain and to evolve because one can exchange single components or
groups of components, knowing which effects this has on the overall system behavior.
Components also help to organize and improve the development process. Different components can be developed separately by different groups. Specialists can
take care of the integration and component frameworks and the infrastructure provided by it. Other engineers can handle configuration and deployment. And components are units of reuse, reducing the development effort, shortening time to
market, and enabling competition on a market of software components.
With this huge motivation, it should be easy to settle on a well-defined component
notion. However, it is not. As illustrated in Section 1.3, components can be identified on all abstraction levels of system descriptions and can be parts of different
views in such a way that there is no natural refinement relation between them.
Following the literature, we have focused on a notion of software components that
links design, implementation, deployment, and system administration. Strictly
speaking, this means that there is a well-understood relationship between a design
component, the program code implementing the component, the deployment units,
and the runtime components. Based on this notion, CBSD can be developed as a
software engineering discipline that exploits the potential of this paradigm with
respect to specification of components, implementation support for components,
quality assurance methods, development processes, tooling, as well as component
reuse and qualification.
Chapter 2 Component software. As software components are parts of software
systems, we started the investigation by looking at complete software systems,
their manifestations, and their different development stages. The same development stages also apply to software components. We discussed component specifi-

161

162

Chapter 8 Review and Current Developments

cation, component implementation, different forms of component compositions,


and deployment. Composition of software components is a complex topic because
it has to integrate the following conceptual aspects:
Stages: Composition can happen at different stages, for example by linking
deployment units or by connecting runtime components to services.
Composition kind: The result of the composition of components C1, ..., Cn
could be a new component C following the same model as the subcomponents
Ci. It might be a new component with different characteristics or no component at all.
Behavior: Composition only works if the components follow the same computation and communication principles.
We discussed four different, common component models, in particular to stress
the fact that component composition is not only about naming conventions, syntactic interfaces, and wiring, but also and at its heart about behavior composition.
Chapter 3 Engineering. Whereas the literature essentially agrees on the core
conceptual aspects of software components, it is not so clear about the relationship between CBSD and software engineering processes. Often the reuse aspect is
stressed and only bottom-up construction from existing components is considered.
From that perspective, techniques for qualification and composition are the main
focus. Other authors relate CBSD very closely to model-driven development,
where a refinement-oriented engineering process is the central issue. In our discussion, we argued for an understanding that is less process- and more architecture-oriented. Software architecture can bridge the gap between
development with reuse, where the architecture is the skeleton for the integration of reusable components and
development for reuse, where the architecture provides the context for component development.
We sketched the relationship between CBSD and adjacent development approaches and stressed the importance of a clear notion of software components for
language, tool, and framework development.
Chapter 4 Frameworks. Component frameworks support a component model
together with techniques and tools for deployment, initialization, and management
of components and their instances. The framework provides a component infrastructure (for example naming services and persistence mechanisms) that can be
reused by all components of the framework. We described the general concepts
underlying component frameworks and illustrated how they are realized in OSGi.
In particular, we explained:

Chapter 8 Review and Current Developments

The distinction between components, containers, framework APIs, and platforms, and how containers provide the context for components and their communication.
Aspects and states of the deployment of bundles and the technical details that
are necessary to handle deployment units in an automated way.
The relationship between program code/packages and service-providing components: A bundle can contain provide packages and components.
The reflexivity and dynamic aspects of components, in particular how a component can listen to events concerning other components.
Along with the explanations, we provided an introduction to OSGi.
Chapter 5 Distribution and heterogeneity. Component frameworks develop
their full strength when they allow the integratation of components written in different languages and deployed on distributed platforms with different operating
systems. We introduced the central concepts of remote method invocation for realizing communication among distributed objects and components. Furthermore,
we explained direct and brokered composition of heterogeneous components. The
main part of the chapter described CORBA's solution to the challenge of distribution and heterogeneity.
Chapter 6 Architecture specialization. We investigated the Enterprise JavaBeans framework as a prominent representative of an architecture-specific component framework. From a conceptual point of view, it is important to understand
the gain that can be achieved by architecture- or domain-specific component frameworks. In particular, different kinds of components can be supported that capture the requirements of specific roles in the architecture or domain. We demonstrated how declarative XML-based descriptors can help in deployment and how
code generation techniques can separate client and component code. Finally, we
explained the important concept of dependency injection and showed how EJB
supports automated dependency injection based on Java annotations.
Chapter 7 Embedded systems. Frameworks for the development of embedded
systems are usually domain-specific. The development of widely used frameworks is hindered by the special technology and real-time constraints in different
domains.

8.2

Current developments

Current work on CBSD technology is concerned with the improvement of existing


component frameworks and the development of new frameworks, in particular
architecture- or domain-specific frameworks. Examples are component models for
compound documents (see, e.g., [13]), interface description languages for scientific computing (see, e.g., [8]), and frameworks for Web development and embed-

163

164

Chapter 8 Review and Current Developments

ded systems. Other areas of development where CBSD makes important contributions are service-oriented architectures (see Subsection 3.4.2 for the relationship
between SOA and CBSD) and product-line engineering. In the remaining paragraphs, we will sketch some current lines of research in the area of CBSD to illustrate open questions and possible future trends.
Software evolution. CBSD integrates aspects of design, implementation, deployment, and runtime management. All these areas are currently supported by
different technologies and tools, e.g.,
UML for modeling and design,
programming language technology for implementation,
version and configuration management systems, package managers, and build
tools for deployment, and
specific runtime managers for administering systems at runtime.
Research on component models and component technology aims to bridge the gap
between these areas in order to simplify software maintenance and evolution. Another important area is that of automated adaptation techniques.
Language support. Many researchers are working on better language support for
CBSD. This includes more powerful module systems and component frameworks
(see, e.g., [15]), better language support for component models in programs,
higher-level language mechanisms for composition and coordination of services
and components, as well as constructs for integrating architectural descriptions
and programs.
Specification and formal methods. Software components should have welldefined interfaces and a behavior that can be described independent of the contexts in which they are used. Research on specification and verification techniques
aims to provide language and verification support for formally specifying component properties and verifying that a component implementation satisfies its specification. Successful steps in this direction have been taken (see, e.g., the SOFA
model in [2]), but they are not yet powerful enough to be applied to practical
component frameworks like OSGi or EJB. Furthermore, there are still a lot of
open questions and challenges in the area of specification and verification of nonfunctional component properties such as quality of service or real-time properties.
Retrieval techniques. Retrieval of reusable components has long been a research
area in software engineering. Retrieval depends on how the components are described. It can be based on meta-data for the classification of components. Here,
the research challenge is to develop appropriate semantic schema technology for
component retrieval. Another line of research aims to retrieve components based
on functional or behavioral search criteria, for example by employing specification matching techniques.

165

Solutions to the Problems

Solutions to the Problems


Self-review questions should help the reader to check after studying a chapter
whether he or she has understood and can recall concepts, facts, and techniques
described in the chapter. Accordingly, most of the questions can be answered by
rereading the corresponding text passage. Thus, the following solutions mainly
refer the reader to the text passage where the topic of the question is treated. If the
question refers to several passages or goes somewhat beyond the text, further explanations are given.

Solutions to the problems of Chapter 1


Solution to problem 1.1:
The motivations are listed in Section 1.2. The corresponding stakeholders are:
Quality: developers performing integration and maintenance; managers and
owners of the producing company; clients buying the product; users.
Flexibility: developers who want to reuse components; managers in the sense
that flexibility reduces time to market (see below).
Evolvability: developers for maintenance; managers and owners of the producing company because maintenance is cheaper; clients, as they can stay
longer with a product.
Separate development: developers and product managers because of clearer
boundaries between different components; management because outsourcing
is easier to handle.
Role separation: developers and product managers, because of clearer
boundaries between different tasks, because of the possibility to assign tasks
to people with different skills, and because the complexity of integration
mechanisms can be overcome by using component frameworks.
Reuse: all stakeholders who profit from cheaper software.
Shortened time to market: managers to get an edge over competitors; clients
and users because they get needed software earlier.
Component market: all stakeholders who profit from cheaper software; small
software companies because it is often easier to compete with large companies
in the area of special small components than to compete in the area of complete software solutions.

Solution to
problem 1.1

166

Solutions to the Problems

Solution to
problem 1.2

Solution to
problem 1.3

Solution to problem 1.2:


See Section 1.3, paragraphs Analysis and Design. Logical component structures could only be used to analyze behavioral requirements. Software components are entities of the design. In CBSD, the software component structure
should be traceable down to the implementation. Thus, the software component
structure must especially take into account system distribution and technology
boundaries, which is not the case for the logical component structure of a system.

Solution to problem 1.3:


Program components can be arbitrary program parts written in a programming
language. Even if we identify program components with modules or packages,
they do not satisfy the explanation of software components given at the beginning
of Section 1.2. Here are some aspects why they do not satisfy the explanation:
A Java package generally has no well-defined interface and its behavior can
often not be described independently.
A package can implement a component, but may also be just a collection of
some code. Without a component model, there is no clear notion of composition.
In summary, packages (or modules) can form the implementation of software
components. But not all packages implement components. Furthermore, software
components have to satisfy additional requirements that go beyond programming.

Solution to
problem 1.4

Solution to problem 1.4:


See the item list in the paragraph Summary at the end of Section 1.3.

Solutions to the problems of Chapter 2


Solution to
problem 2.1

Solution to problem 2.1:


See the item list at the beginning of Chapter 2 and the four component models
with different execution and communication behavior described in Section 2.4.

Solution to
problem 2.2

Solution to problem 2.2:


The text distinguishes three kinds of composition. See the paragraph "Component
software" in Section 2.2.

167

Solutions to the Problems

Solution to problem 2.3:


As further described in the paragraph "Components, state, interface extension",
components are similar to classes in that they have instances, state, an identity,
and a lifetime. They are different in that the instance of a component may consist
of several objects or no object at all, whereas the instance of a class is exactly one
object. Interface and behavior of components can be much more complex than the
interface of a class; especially the services of a component can be implemented by
different objects.

Solution to problem 2.4:

Solution to
problem 2.3

Solution to
problem 2.4

Separate development needs clear interfaces between the component and program
parts that should be developed separately and a precise description of the component's behavior. This is what a specification provides. The specification can be
considered as a contract between clients of the component and the providers or
developers of the component.

Solution to problem 2.5:

Solution to
problem 2.5

Threads are not component-local entities; they can be created in one component
and execute methods/code of other components (also see Section 2.4.2). Without
appropriate synchronization, several threads can execute concurrently within a
component instance and cause data races and state inconsistencies. On the other
hand, without knowing a component's context, conservative synchronization may
lead to unexpected deadlocks.
The declaration of the attributes as volatile is necessary because the corresponding
instance variables are written and read by different threads. Without the volatile
declaration, the synchronization of Java does not guarantee that the thread created
by the Thermostat component observes the modifications to the variable desiredTemp done by the thread of the ControlPanel.

Solution to problem 2.6:


Synchronization is only needed at the message queues. The library classes for the
message queues (implemented by expert developers) establish correct synchronization (see paragraph "Implementation", Subsection 2.4.3).

Solution to
problem 2.6

168

Solutions to the Problems

Solution to
problem 2.7

Solution to problem 2.7:


As long as there are no deadlocks within single components, there are no deadlocks in component systems built according to CSC.
Without further sophistication, CSC is inefficient if there are components with
very different computation time for a step, because the next step has to wait until
the slowest component has finished the last step.

Solutions to the problems of Chapter 3


Solution to
problem 3.1

Solution to problem 3.1:


Explanations and the relationship to top-down and bottom-up development are
given in Subsection 3.3.1. Development for reuse is also discussed in the context
of component selection and adaptation (see Section 3.3.4). The role of architecture
is explained at the beginning of Section 3.4.

Solution to
problem 3.2

Solution to problem 3.2:


The three main reasons for software evolution are described at the beginning of
Subsection 3.3.2. The last paragraph of the subsection discusses scenarios where
CBSD is of no help for evolution.

Solution to
problem 3.3

Solution to problem 3.3:


The answers are given in Subsection 3.3.3.

Solution to
problem 3.4

Solution to problem 3.4:


The explanation of white-box, gray-box, and black-box adaptation is given in the
second half of Subsection 3.3.4. The main difference is to which extent we can
access and modify the implementation/representation of the component that
should be adapted.

Solution to
problem 3.5

Solution to problem 3.5:


See the paragraph "Product-line engineering" in Subsection 3.4.2. In summary,
product-line engineering centers on the development of software artifacts for all
software engineering phases to make a family of software products as manageable
as possible. Different techniques are used for this goal and component technology
can be one of the techniques. The focus of CBSD is the development of compo-

169

Solutions to the Problems

nents and the construction of software from components independent of whether


this is used to realize product lines or to build service-oriented architectures.

Solutions to the problems of Chapter 4


Solution to problem 4.1:

Solution to
problem 4.1

Two components are equal if they have the same name, specification, and body.
Thus, it is not sufficient that they have or satisfy the same specification (cf. Subsection 4.2.4).

Solution to problem 4.2:

Solution to
problem 4.2

Composition occurs on the level of the component representations (bundling), at


deployment time when implementation parts are linked (deploying), and at runtime when component instances are connected by passing references around (connecting). Details are given in Subsection 4.2.3.

Solution to problem 4.3:

Solution to
problem 4.3

The transition from INSTALLED to RESOLVED corresponds to linking in a


software construction with compiler, linker, and loader.

Solution to problem 4.4:

Solution to
problem 4.4

Bundles can provide packages (static aspect) and services (dynamic aspect), see
the beginning of Subsection 4.3.2 for an explanation and the rest of the subsection
for examples. The dynamic aspect reflects the provided interfaces and services of
components, that is, it reflects what clients want to have. The static aspect is necessary to share and effectively structure the code that components need to provide
the services.

Solution to problem 4.5:


The dependency relation can be cyclic, e.g., if component C1 uses a service provided by C2 and the other way round. In many frameworks, it is difficult for a
component to observe that it is part of a cycle, because a component C often does
not know the components that provide the services that it, C, uses. The typical
problem of cyclic dependencies is that it can lead to non-termination.

Solution to
problem 4.5

170

Solutions to the Problems

Solutions to the problems of Chapter 5


Solution to
problem 5.1

Solution to problem 5.1:


The terms and their functions are explained in Section 5.2, paragraph "Concepts
of RMI". A stub is a placeholder for the server object within the client process.
The skeleton is the proxy for the server object within the server process that realizes the communication with the outside world. Stubs, skeletons, and parameter
marshaling are techniques used to bridge the gap between different operating
processes.

Solution to
problem 5.2

Solution to problem 5.2:


In RMI, a remote reference R can be obtained by a client either via a lookup in a
registry in which R is registered under some name or by getting R as return value
or parameter of a method call (see Section 5.2). CORBA also allows storing and
retrieving IORs, its version of remote references, into and from files. This is possible and supported because IORs have a well-defined textual representation (in
RMI, this can only be done with certain restrictions). Furthermore, CORBA provides a trading service that allows obtaining IORs for services described and determined by a property scheme (see last paragraph of Subsection 5.4.3).

Solution to
problem 5.3

Solution to problem 5.3:


The different approaches are described at the beginning of Section 5.3. Brokered
composition needs an IDL to translate the data representations of implementations
in one language to the corresponding representations in the other language (see
Section 5.3 for the general explanations and Section 5.4.2 for an example).

Solution to
problem 5.4

Solution to problem 5.4:


The mechanism of object adapters is explained in Subsection 5.4.1. Object adapters are an additional indirection between client objects and servants. If a service
provider wants to offer thousands of servants, but these servants are not needed at
every moment, it is reasonable to shut down servants that are not needed. For clients, this resource saving is transparent. If they invoke a method on an IOR of a
shutdown servant, the object adapter mechanism will automatically restart the
servant process. Thus, the object adapter mechanism is important to scale to thousands of servants or to prevent expending unnecessary resources.

171

Solutions to the Problems

Solution to problem 5.5:

Solution to
problem 5.5

The IFR provides the meta-data about IDL types and makes it available to programs using CORBA (see the paragraph "Reflection capabilities" of Subsection
5.4.3).

Solution to problem 5.6:

Solution to
problem 5.6

The CORBA trading service (see last paragraph of Subsection 5.4.3) is designed
to select a service. A component can provide multiple services and has additional
crucial properties, such as its implementation language and its platform restrictions. To use trading services for component selection, these and additional properties have to be covered by the Trader Constraint Language as well. In addition, a
download, adapt, and deploy mechanism is desirable, which is not needed for service trading.

Solutions to the problems of Chapter 6


Solution to problem 6.1:

Solution to
problem 6.1

An architecture-specific framework can tailor the services and the supported component kinds to the needs of the architecture and application domain. All generic
aspects of the architecture can be covered by the framework. The EJB framework,
for example, provides generic support for persistence, transactions, and security,
and the PECOS framework is tailored towards the needs of field devices (Subsection 7.3.2). This kind of role separation between framework developers and application developers has a huge potential for saving development efforts and costs.

Solution to problem 6.2:

Solution to
problem 6.2

The program parts of the beans are listed at the beginning of Subsection 6.3.2.
Their roles are explained in the rest of Subsection 6.3.2 and the succeeding subsections.

Solution to problem 6.3:


The reasons are described in the item list at the end of Subsection 6.3.3. In summary, the delegation mechanism is used
to establish an encapsulation discipline between beans and between clients and
beans

Solution to
problem 6.3

172

Solutions to the Problems

and to keep the code for the business logic realized in beans separate from the
infrastructure code.
Solution to
problem 6.4

Solution to problem 6.4:


The simplifications could be achieved by using the knowledge of the architecture
and the parameters of standard application scenarios. Thus, as long as the application fits into these scenarios, only the parameters must be provided, and this can
be realized effectively with appropriate code annotations. Based on the annotations, the needed compositions and configurations can be generated or extracted
from the annotations at runtime.
More generally, if the architecture and parameters of a class of applications are
known, the application developer can focus on the application logic. The rest can
be provided by the framework or generated from additional annotations or markups.

Solution to
problem 6.5

Solution to problem 6.5:


The reason of the restrictions is to guarantee the encapsulation properties and to
avoid deadlocks:
Passing out "this" would provide direct access to the instance of the bean
class, bypassing the security mechanisms that the EJB framework puts around
these instances.
Static variables also break encapsulation, as every program can access these
variables.
Threads can cause deadlocks.
GUIs can cause all of the above problems.
Use of reflection would also make it possible to bypass the security mechanisms.
And messing around directly with class loading and security mechanisms can,
of course, break the otherwise given guarantees of the framework.

Solution to
problem 6.6

Solution to problem 6.6:


Constructor injection is simple and avoids method overhead, but is not suitable
in cases with many dependencies, in cases where not all dependencies are injected
at the beginning, or in cases of cyclic dependencies. Setter injection is easy to understand and to handle. It does not allow structuring dependencies and can lead to
a large number of methods. Interface injection is the most flexible technique, but
comes with the overhead of defining and documenting the interfaces.

173

Solutions to the Problems

Solutions to the problems of Chapter 7


Solution to problem 7.1:
Here is a list of features, aspects, and characteristics (look for more!):
1. Pin: non-hierarchical model with static links for embedded systems; component model designed to build predictable embedded non-distributed software
whose components can be specified with UML statecharts.
2. PECOS: hierarchical model specifically designed for fields devices. Allows
modeling hardware as event components so that the complete hardware/software system can be captured and analyzed.
3. Koala: hierarchical model for consumer electronics with special support for
handling product lines. In particular, it supports so-called diversity interfaces
for describing variants and a partial evaluation mechanism for getting rid of
code that is not needed in a specific product configuration.
4. AUTOSAR: AUTOSAR goes beyond component frameworks. In particular, it
aims to include descriptions of ECUs and system-wide information as well as
aspects of architecture-driven development.

Solution to
problem 7.1

174

Solutions to the Problems

175

Index

Index
A
ACC

C
37, 40

callback

actions

37

callback scenario

activator class

84

CBSD

1, 54, 55, 61

active

26

CCM

122

active component

v, 156

29

changing requirements

57

activity

37

CIDL

actor

18

code categories

36

code maintenance

57

code view

64

adaptation

v, 59

administration

aggregation

75

analysis

123

Common Object Request Broker Architecture 109


component

23, 129

AOSD

66

component activity

26

Apache Felix

83

component adaptation

58

application deployer

component body

77

application provider

component composition

31

application server

127

component container

application-independent

129

component description

31

63

component framework

vi, 49, 71

architectural views
architecture

architecture description language

157

architecture-specific

127

component header

77

Component Implementation Definition


Language

123

aspect-oriented software development

66

component infrastructure

48

asynchronous

40

component instance

31

AUTOSAR

component kind
component model

131, 156
vi, 15, 37, 48, 129, 155, 157

component provider

bean component

130

component qualification

59

bean composition

147

component retrieval

58

bean instance

130

component selection

58

bean-managed persistence

131

component server

behavior

v, 162

component software

19
74

black-box

59

component specification

black-box reuse

72

component-based software development

brokered composition
bundle event
bundling

108
96
75, 82

124

v, 1,

53, 55
component-based software system
composition

v, 54
19

business application

127

composition kind

vi, 162

business logic

128

composition mechanism

75

conceptual view

64

configuration

18

176

Index

configuration mechanism

81

entity bean

connecting

76

environment

connector

38, 42

consumer electronics
container

153, 157

73, 81, 124, 129, 131, 155, 163

container-managed persistence

130
vii, 18

event component

156

evolution

57

evolvability

131

execution model

154

context

77

execution view

64

contract

10

convention

145

CORBA

vi, 100, 109, 153

CORBA Component Model

122

facet
field device

coupling

60

flexibility

cross-cutting concern

66

framework event

CSC

37, 46

153, 156
3
96

framework provider,

data tier

128

dependency injection
deployment

122

vi, 101, 147


vi, 18, 35, 75, 80, 129, 136

deployment descriptor

vi

deployment mechanism

generation technique
glue code

59

design

heterogeneity

design decision

heterogeneous

56, 60, 162

gray-box

81

development for reuse

vii, 32, 46

goal

138

deployment rule

138

108, 163
108

heterogeneous composition

20

development process

11

hierarchical

development stages

vii

hierarchical component

99

hierarchical composition

20

development with reuse

56, 60, 162

direct composition

108

distribution

100, 103, 163

diversity

154

diversity interface

157

divide and conquer

53

dynamic

64

dynamic aspect

91

dynamically required

147

IDL

109

IFR

120

implementation
implementation layer
implementation repositories
information hiding

Eclipse Equinox

83
49, 101, 129, 153, 164

embedded system
encapsulation
Enterprise JavaBeans
Enterprise JavaBeans (EJB)

100, 109, 112

IDL mapping

IMR

E
EJB

156, 157

infrastructure
initially required

vii, 8, 10, 29
72
111
111, 119
53
10, 96
147

vii, 153

integration

61

53

interceptor

119

129

interface

vii

interface automaton

44

vii

177

Index

interface definition language

109

interface definition language (IDL)

vii

Object Management Group

109

object request broker

105

interface description language

100

OEM

vii

Interface Repository

120

OMG

109

Interoperable Object Reference

105

open composition

inversion of control

101

ORB

IOR

105, 111

vii, 49, 71, 83, 153, 164


58

20, 32
package management

L
language mapping

111

outsourcing

K
kinds of composition

OSGi

20

112, 114, 118

91

Palladio

154

partial evaluation

157

passive

26

layered

17

logic tier

128

passive component

viii

Passive component

156

PECOS

156

logical component
logical view

64

persistence

phases of software development

maintenance

manifest file

84

market of software components


marshaling

viii, 146

Pin

155

platform

viii, 17, 73, 81

platform assumption

153

105

platform evolution

57

MDA

66

port

message queue

40

predictable

viii, 22, 38, 122, 156


155

message-driven bean

130

prerequisite

method binding

107

presentation tier

mobile phone

153

product-line engineering

model variable

23

program component

model-driven development

67

provided port

module interconnection view

64

motivation

2, 161

MTC

37, 39

multi-language

108, 112

multi-language support

100

multi-threading

39

name

74

name space

74
154, 164

O
object adapter

12
128

111

67, 157
viii
22

Q
qualification

11

quality

quality assurance

11, 60

quality control

60

quality of service

non-functional

120, 154

R
receptable

122

reflection

120

reflexivity

99

remote method invocation

104

remote method invocation (RMI)

viii

178

Index

remote procedure call

103

stage

19, 162

remote reference

106

state

23
23

rendezvous

43

state space

required port

22

stateful session bean

130

stateless session bean

130

requirement

5, 153, 157

resource

153

static

64

resource limitation

154

static aspect

91

retail store example

retrieval

164

reuse

3, 53

rich component

154

RMI

104

role separation

3, 58

stub

105

synchronization

39

system administrator

T
TCF

76

RPC

103

ThermoControl example

20

rule

145

thread

39

S
SCC
scheduling
security
sensor
separate development

37, 43
154, 156

time to market

trading service

121

transaction

147

transition

98, 147

18
3

44

UML statechart

155

unmarshaling

105

servant

105

service

76

service event

96

service-oriented development

66

variant

157

shared variable

156

vehicle

153

skeleton

105

verification

164

smart card

153

version number

74

versioning

98

view

63

SOA

65

software architecture

60, 61

software component

viii, 2, 6, 15, 74

software evolution

57, 164

software modeling

10

software specification

10

software system
specification

16, 18
22, 164

views on software component


virtual

9
64

W
white-box

59

Index

179

You might also like