[PDF version] [PS version]



The Fractal Component Model
fractal.png



Authors:       
E. Bruneton          (France Telecom R&D)
T. Coupaye          (France Telecom R&D)
J.B. Stefani          (INRIA)

Released    September 12, 2003
Status   Draft
Version   2.0-2

General Information
Copyright 2002-2003 France Télécom S.A.
28, chemin du vieux chêne, 38243, Meylan Cedex, France.
Copyright 2002-2003 INRIA.
655 avenue de l'Europe, ZIRST, Montbonnot St Martin, 38334 Saint-Ismier Cedex, France.
All rights reserved.
Trademarks
All product names mentioned herein are trademarks of their respective owners.
Disclaimer of warranties
The specification is provided "as is". The specification could include technical inaccuracies or typographical errors. Changes are periodically added to the information therein; these changes will be incorporated into new versions of the specification, if any. France Telecom and INRIA make no representations or warranties, either express or implied, including but not limited to, warranties of merchantability, fitness for a particular purpose, or non-infringement that the contents of the specification are suitable for any purpose or that any practice or implementation of such contents will not infringe any third party patents, copyrights, trade secrets or other rights.

Contents

1  Introduction
    1.1  Rationale
    1.2  Overview
2  Foundations
    2.1  Interface Definition Language
    2.2  Naming and binding
3  Introspection
    3.1  External component structure
    3.2  Component introspection
    3.3  Interface introspection
4  Configuration (introspection & intercession)
    4.1  Internal component structure
    4.2  Attribute control
    4.3  Binding control
    4.4  Content control
    4.5  Life cycle control
5  Instantiation
    5.1  Factories
    5.2  Templates
    5.3  Bootstrap
6  Typing
    6.1  Contingency and cardinality
    6.2  Type system
    6.3  Sub typing relation
7  Options
    7.1  Conformance levels
    7.2  Extensions
8  Example
    8.1  Instantiation
    8.2  Reconfiguration
A  Mappings
B  Glossary
C  Change History

1  Introduction

1.1  Rationale

The number of industrial component models, such as the Component Object Model (COM), the Distributed Component Object Model (DCOM), the Java Beans and Enterprise Java Beans (EJB) models, the CORBA Component Model (CCM), or the San Francisco model, as well as the number of academic component models and Architecture Description Languages (ADLs), clearly shows that component-based programming has emerged as an important topic in software engineering and distributed computer systems. But the existence of all these component models, their complexity, their lacks, the incompatibilities between them, and the fact that each model addresses a specific target (from "client side components" for graphical interfaces to "server side components" for business processing), also shows that current component models are still immature.
In this context, the Fractal component model is an attempt to isolate the common patterns and concepts between the existing industrial and experimental component models, to define these patterns and concepts precisely, and to organize them clearly. The goal is to get a better understanding of the existing models and of the relations between them, but also to define a new, modular and extensible component model, directly based on these concepts and patterns, to implement this new component model in various component platforms and languages, and to implement tools based on this model (such as configuration and deployment tools).

1.2  Overview

Main goals of the Fractal component model are to implement, deploy and manage (i.e. monitor and dynamically reconfigure) complex software systems. These goals motivate the main features of the Fractal model: composite components (to have a uniform view of applications at various abstraction levels), shared components (to model resources), introspection capabilities (to monitor a running system), and configuration and reconfiguration capabilities (to deploy and dynamically reconfigure an application). But another goal of the Fractal model is to be applicable to many software, from embedded software to application servers and information systems. Unfortunately, the advanced features of the Fractal model have a cost that is not always compatible with the limited resources of constrained environments.
In order to achieve these contradictory goals, the Fractal component model is not defined as a big, fixed specification that all Fractal components must follow, but rather as an extensible system of relations between well defined concepts and corresponding APIs that Fractal components may or may not implement, depending on what they can or want to offer to other components. This set of specifications can be organized, and is presented in this document as increasing "levels of control", i.e. in increasing order of reflective capabilities (introspection and intercession).
At the lowest level of control, a Fractal component is a runtime entity that does not provide any control capability to other components, and is therefore like an object (such a component can be used in only one way, namely by calling methods on it). In fact, an object is a Fractal component without any control capability (see section 2). This feature is useful to handle cases where components have to be connected to legacy software.
At the next level of control capability, which can be called the external "introspection" level (see section 3), a Fractal component can provide a standard interface, similar to the IUnknown interface in COM, that allows one to discover all its external interfaces or, in other words, its boundary (like an object, a Fractal component can provide several interfaces).
At the next level of control capability, which can be called the "configuration" level (see section 4), a Fractal component can provide control interfaces to introspect and modify its content, i.e. what is inside its boundary. In the Fractal model, this content is made of other Fractal components, called its sub components, bound together through bindings. A Fractal component can therefore choose to provide or not an interface to control the set of its sub components, the set of bindings between these sub components, and so on.
In addition to these control capabilities, the Fractal model also specifies a framework for the instantiation of components (see section 5), and a simple type system for Fractal components (see section 6). Like the above control capabilities, the instantiation framework and the simple type system are optional. In fact, in the Fractal model, everything is optional. This has advantages and drawbacks, which are discussed in section 7.
As a result of this modular and extensible organization (anyone is free to define its own control interfaces, in order to provide new introspection and intercession capabilities), and given the fact that the Fractal component model is not tied to a specific language, Fractal components can be used in very different situations, from operating systems to middleware platforms, from graphical user interfaces to information systems, and from highly optimized but unreconfigurable configurations, to less optimized but highly dynamic and reconfigurable systems or applications.

2  Foundations

At the lowest control capability level, a Fractal component does not provide any introspection or intercession function to other components. Such a component, called a base component, can be used in only one way, namely by invoking operations on its component interfaces. A component interface is an access point to a component that implements a language interface. Component interfaces and language interfaces should not be mixed up: a component interface is an access point that implements a language interface; a language interface is a type. Despite of this risk, sentences such as "a component has an interface that implements the language interface X" will often be abbreviated, in the rest of this document, into "a component has an interface X", in order to improve readability.
This section defines an Interface Definition Language for language interfaces implemented by Fractal component interfaces. It also defines a framework to get access to component interfaces.

2.1  Interface Definition Language

In order to allow Fractal components implemented in distinct programming languages to interoperate, some common conventions are necessary. More precisely, an Interface Definition Language (IDL) is necessary, which must then be completed with a standard calling convention (for local and remote calls), and with appropriate mappings from the IDL to existing programming languages. This version of the specification only deals with the first requirement, namely the IDL. It also defines a Java and a C mapping (see Appendix A) but, since a standard calling convention has not been defined yet, true interoperability is not a goal of these mappings.
Several IDLs already exist but, unfortunately, the existing IDLs do not satisfy the Fractal needs of minimality and simplicity. For instance, the CORBA IDL is not satisfactory because each interface inherits implicitly from Object, which has many methods, and therefore many dependencies to many other interfaces. The Fractal model therefore defines its own IDL, called the Fractal IDL, based on the syntax and semantics of an existing language, namely Java (in order to avoid defining a new IDL from scratch).
A Fractal IDL interface definition is a Java interface definition with the following restrictions:
and with the following extensions:
By definition, an IDL only defines interfaces. This is why Java class types are not allowed, including Object and String. These last two classes are therefore replaced with new keywords (and not with equivalent interfaces, so that the methods defined in Object and String are not mandatory - some methods of these classes, such as wait and notify, can be complex to implement, especially in constrained environments).
Exceptions are allowed in operation declarations, but are not considered as classes, as in Java: they are instead considered as abstract names, denoting categories of errors. These exceptions can be mapped to exception mechanisms, to additional parameters, or to anything else, depending on the concrete language to which the Fractal IDL is mapped (see Appendix A). The UnknownException exception is an implicit exception, which may be thrown by any operation.
Fractal IDL interface definitions must follow the same rules as Java interface definitions. For example, an interface cannot contain several operations with the same name and the same parameter types, but with different return types, or with incompatible throws clauses.
Note that, as everything in the Fractal model, the Fractal IDL is optional. This means that Fractal component interfaces do not have to conform to this IDL, in particular if they do not have to be interoperable with components implemented in different programming languages.

2.2  Naming and binding

In order to invoke operations on a component interface, one must first identify the interface to be called, and then get an access to this interface. This section defines a framework for doing so, based on names, naming contexts and binders (see Figure 1). This framework is mainly designed to access remote interfaces, but can also be used in a single address space.
package org.objectweb.naming;
interface Name {
   NamingContext getNamingContext ();
   byte[] encode () throws NamingException;
}
interface NamingContext {
   Name export (any o, any hints) throws NamingException;
   Name decode (byte[] b) throws NamingException;
}
interface Binder extends NamingContext {
   any bind (Name n, any hints) throws NamingException;
}
Figure 1: Naming API
A name designates a component interface. Names can take many forms, such as Java references or Interoperable Object References (IORs). A name does not necessarily give direct access to the interface it designates (a CORBA IOR does not give direct access to the designated remote interface; on the contrary, a Java reference can be used directly to call methods on the designated interface). A name is represented by the Name interface.
A name is always associated to a naming context, and is generally invalid outside this context. For example the naming context of a Java reference is the Java Virtual Machine (JVM) in which the designated object resides. This name is meaningless outside this context and, in particular, in another JVM. The naming context of an IOR is the CORBA IOR "name space". An IOR is meaningless outside this context and, in particular, in the Java RMI over JRMP context. The Name interface specifies a getNamingContext operation, which returns the naming context of the name on which this operation is invoked.
A name can be serialized in many forms, such as a string or a byte array. For example a Java reference can be serialized as a string representing the memory address of the object, in decimal or hexadecimal form. An IOR can be serialized as a string containing a host name or IP address, a TCP port number, and an object key. In serialized form, a name can be sent over a network, or stored in a file or a database. The Name interface specifies an encode operation, which returns an encoded form of the name on which this operation is invoked, as a byte array.
A naming context is represented by the NamingContext interface. A naming context creates and manages names in its context. In particular, a naming context can create a name for a given component interface. The NamingContext interface specifies an export operation for doing that: this operation takes as argument a component interface and optional hints, and returns a name for this interface. A naming context can also deserialize names in serialized form. The NamingContext interface specifies a decode operation for doing that: this operation takes as argument a serialized name, as a byte array, and returns the corresponding name.
In order to access the interface designated by a name, a binding must be established to this interface. For example, in order to access a remote interface designated by an IOR, a socket must be opened to send an invocation message to the remote interface. These bindings are created by binders. A binder is represented by the Binder interface, which extends the NamingContext interface. This interface specifies a bind operation that takes a name as parameter, creates a binding to the interface designated by this name, and returns a delegate (or proxy) of this interface, or the interface itself, to invoke operations on it.
The org.objectweb.naming.NamingException exception must be thrown when an error occurs in the operations of the Name, NamingContext and Binder interfaces.

3  Introspection

At the next control capability level, beyond the base level where components do not provide any control function, a Fractal component can provide introspection functions to introspect its external features, i.e. its boundary. This section defines more precisely the external features of Fractal components, and specifies the interfaces related to the introspection of these features. The interfaces related to the introspection (and reconfiguration) of the internal features of Fractal components are specified in the next section.

3.1  External component structure

Depending on the level of observation, or scale, a Fractal component can be seen as a black box or as a white box. When seen as black box, i.e. when its internal organization is not visible, the only visible details of a Fractal component are some access points to this black box, called its external interfaces (see Figure 2). Each interface has a name, in order to distinguish it from the other interfaces of the component (a component can have several interfaces implementing the same language interface). All the external interfaces of a component must have distinct names, but two interfaces in two distinct components may have the same name. One may distinguish two kinds of interfaces: a client (or required) interface emits operation invocations, while a server (or provided) interface receives them.
external-view.png
Figure 2: External view of a Fractal component
The interfaces of a component can be introspected with two language interfaces, specified in the next two sections: one to get the list of interfaces of a component, and one to introspect the interfaces themselves. These two interfaces are of course optional, as everything in the Fractal model: a component can provide both interfaces, only the first one, or none of them.

3.2  Component introspection

In order to discover the external interfaces of a component, a component can provide an interface that implements the Component interface (see Figure 3). This language interface provides two operations named getFcInterfaces and getFcInterface, that can be used to retrieve the interfaces of the component. The first operation takes no arguments, and returns an array containing all the external interfaces, either client or server, of the component, including the Component interface. The second operation takes the name of an interface as parameter, and returns this interface, if it exists.
package org.objectweb.fractal.api;
interface Component {
   any[] getFcInterfaces ();
   any getFcInterface (string itfName) throws NoSuchInterfaceException;
   Type getFcType ();
}
interface Type {
   boolean isFcSubTypeOf (Type t);
}
Figure 3: Component introspection API
The getFcInterfaces and getFcInterface operations return references that give access to requested interfaces. In other words, the references returned by these operations can be used directly, after an appropriate cast, to invoke operations on the component's server interfaces (no explicit binding is needed). For example, if a component has a server interface named account implementing the language interface Account, then the getBalance operation of this interface can be invoked with a code like ((Account)c.getFcInterface("account")).getBalance(), where c is a reference to the Component interface of the component.
The Component interface also provides a getFcType operation, which returns the type of the component, as a Type reference. This interface defines a minimal notion of type, which actually defines only one operation named isFcSubTypeOf, whose role is to test if a given type is a sub type or not of another type. This interface can be extended to define more useful type systems for components and component interfaces, such as the one defined in section 6.
The org.objectweb.fractal.api.NoSuchInterfaceException exception must be thrown in the getFcInterface operation when a requested component interface is not found.
A component interface implementing Component must be named component.

3.3  Interface introspection

By default the references returned by the getFcInterface and getFcInterfaces operations provide access to the requested interfaces, and nothing more. In particular, it is impossible to find the names of these interfaces. In order to provide such interface introspection functions, a component can ensure that the references returned by the above operations are castable into the Interface type (see Figure 4). This interface specifies four operations to get the name of a component interface, to get its type (as a Type reference), to get the Component interface of the component to which it belongs, and to test if the interface is internal or not (see section 4.1).
package org.objectweb.fractal.api;
interface Interface {
   string getFcItfName ();
   Type getFcItfType ();
   Component getFcItfOwner ();
   boolean isFcInternalItf ();
}
Figure 4: Interface introspection API
Note that the getFcItfOwner operation allows one to discover all the interfaces of a component from any interface of this component, and not only from its Component interface. For example, if a is a reference to the Account interface of a such component, the Component interface of this component can be retrieved with a code like ((Interface)a).getFcItfOwner(), if the component provides interface introspection functions. The result can then be used to get the reference of any other interface of the component.
The Interface interface inherits from the Name interface, in order to show that component interfaces can be sent over a network, as names. Furthermore, this name can be seen as a compound name, made of a name that identifies a component (returned by getFcItfOwner), and of a local name (returned by getFcItfName) that identifies an interface inside this component. Note that simple string names are used for local interface names, although it would have been possible to use instead the general naming and binding framework defined in section 2.2.

Notes

4  Configuration (introspection & intercession)

At the next level of control capability, beyond the "introspection" level where components provide interfaces to introspect their external features, a Fractal component can provide control interfaces to introspect and reconfigure its internal features. This section defines these internal features, and specifies some possible interfaces to introspect and reconfigure them.

4.1  Internal component structure

Internally, a Fractal component is formed out of two parts: a controller (also called membrane), and a content (see Figure 5). The content of a component is composed of (a finite number of) other components, called sub components, which are under the control of the controller of the enclosing component. The Fractal model is thus recursive and allows components to be nested (i.e. to appear in the content of enclosing components) at an arbitrary level. A component that exposes its content is called a composite component. A component that does not expose its content, but has at least one control interface (see below), is called a primitive component. A component without any control interface is called a base component (see section 2).
internal-view.png
Figure 5: Internal view of a Fractal component
The controller of a component can have external and internal interfaces. External interfaces are accessible from outside the component, while internal interfaces are accessible only from the component's sub components. All the external interfaces of a component must have distinct names, all its internal interfaces must have distinct names, but a component can have an external and an internal interface of the same name. A functional interface is an interface that corresponds to a provided or required functionality of a component, while a control interface is a server interface that corresponds to a "non functional aspect", such as introspection, configuration or reconfiguration, and so on. By convention, an interface is considered to be a control interface if its name is equal to component, or ends with -controller. All other interfaces are considered to be functional interfaces.
The controller of a component embodies the control behavior associated with a particular component. In particular, a component controller can:
Each component controller can thus be seen as implementing a particular semantic of composition for the component's sub components. The control capability of a controller is not limited by the model. For instance, it can be mainly interception-based as in industrial component frameworks containers for instance; or it can be void (i.e. no control is exercised - in this case, the controller can still be useful for it can provide a representation of its content and manifest a containment relationship).
A component may appear in the content of (be shared by) several distinct enclosing components (see section 4.4 and Figure 9). A component that is shared among two or more distinct components is subject to the control of their respective controllers. The exact semantics of the resulting configuration (e.g. which control behavior is enacted) is in general determined by an encompassing component that encloses all the relevant components in the configuration.
A binding is a communication path between component interfaces. The Fractal model distinguishes between primitive bindings and composite bindings. A primitive binding is a binding between one client interface and one server interface, in the same address space, which means that the operation invocations emitted by the client interface should be accepted by the specified server interface. A primitive binding between a client interface c and a server interface s of two components C and S must verify one of the following constraints (see Figure 5):
In addition to these structural constraints, which ensure that primitive bindings cannot "cross" component boundaries except through interfaces, a primitive binding can be established between a client and a server interface only if the server interface can accept at least all the operation invocations that the client interface can emit. In other words, the (language) type of the server interface must be a sub type of the type of the client interface (the two interfaces can of course be of the same type since a sub typing relation must be reflexive). The last constraint is that a client interface can be bound to at most one server interface, while several client interfaces can be bound to the same server interface.
A composite binding is a communication path between an arbitrary number of component interfaces, of arbitrary language types. These bindings are represented as a set of primitive bindings and binding components (stubs, skeletons, adapters, ...). A binding component is a normal Fractal component, whose role is dedicated to communication. Binding components are also called connectors: hence Fractal does have connectors, although this concept is not a core concept here, as component or interface - this is why there is no special API for them. It should be noted that the binding concept defined here is exactly is the same as the binding concept defined in section 2.2: in particular, primitive bindings correspond to local bindings, i.e. to bindings inside a single address space.

4.2  Attribute control

An attribute is a configurable property of a component, such as the text or color of a button, or the maximum size of a pool or cache component. Attributes are generally of primitive type, and are used to configure the state of components without needing to use bindings (it is possible to configure the text of a button, for example, by binding this button component to a text component; but this is overly complex for what is needed). A component can provide an AttributeController interface to read and write its attributes from outside the component (see Figure 6).
package org.objectweb.fractal.api.control;
interface AttributeController { }
Figure 6: Attribute control API
In this case, the component must actually provide a sub interface of this interface, since the AttributeController interface is in fact empty. This sub interface must contain one getter and/or setter operation per configurable attribute. For example:
It is a requirement of this specification that setters and getters must follow the lexicographic and typing conventions introduced informally in the example above with respect to names and signatures of setters and getters (these conventions are those of the Java Beans component model).
A component interface implementing AttributeController must be named attribute-controller.

4.3  Binding control

A component can provide the BindingController interface to bind and unbind its client interfaces to other components through primitive bindings (see Figure 7).
package org.objectweb.fractal.api.control;
interface BindingController {
   string[] listFc ();
   any lookupFc (string clientItfName)
      throws NoSuchInterfaceException;
   void bindFc (string clientItfName, any serverItf)
      throws NoSuchInterfaceException, IllegalBindingException, IllegalLifeCycleException;
   void unbindFc (string clientItfName)
      throws NoSuchInterfaceException, IllegalBindingException, IllegalLifeCycleException;
}
Figure 7: Binding control API
This interface defines the following operations:
These operations may throw a NoSuchInterfaceException exception if a specified client interface does not exist, an IllegalLifeCycleException exception when a component is not in an appropriate state to perform an operation, and an org.objectweb.fractal.api.control.IllegalBindingException exception in case of other errors related to bindings.
A component interface implementing BindingController must be named binding-controller.
Composite bindings are created with the general naming and binding framework (see section 2.2). For example, in order to bind two component interfaces that are not in the same address space, a binding component must first be created between the two interfaces, by using the naming and binding framework (typically, the server interface will be exported in some distributed NamingContext, the returned Name will be encoded, sent over the network, decoded, and finally a Binder will be used to create the binding component from this name; all this can be done explicitly or implicitly, as the effect of passing an interface reference in a remote operation invocation). The BindingController interface will then be used to create the primitive bindings between the client interface and the binding component, and between the binding component and the server interface.

4.4  Content control

A component can provide the ContentController interface to add and remove sub components in this component (see Figure 8).
package org.objectweb.fractal.api.control;
interface ContentController {
   any[] getFcInternalInterfaces ();
   any getFcInternalInterface (string itfName) throws NoSuchInterfaceException;
   Component[] getFcSubComponents ();
   void addFcSubComponent (Component c)
      throws IllegalContentException, IllegalLifeCycleException;
   void removeFcSubComponent (Component c)
      throws IllegalContentException, IllegalLifeCycleException;
}
interface SuperController {
   Component[] getFcSuperComponents ();
}
Figure 8: Content control API
This interface defines three operations to get the list of sub components of a component, and to add and remove sub components in a component:
A given component can be added to several other components. Such a component is said to be shared between these components. Shared components are useful, paradoxically, to preserve component encapsulation. Consider, for example, a menu and a toolbar components (see Figure 9), with an "undo" toolbar button corresponding to an "undo" menu item. It is natural to represent the menu items and toolbar buttons as sub components, encapsulated in the menu and toolbar components, respectively. But, without sharing, this solution does not work for the "undo" button and menu item, which must have the same state (enabled or disabled): these components, or an associated state component, must be put outside the menu and toolbar components. With component sharing, the state component can be shared between the menu and toolbar components, in order to preserve component encapsulation. Shared components are also useful to help separate "aspects" in component based applications. For example, a shared logger component allows one to avoid adding a Logger client interface to many components.
sharing.png
Figure 9: Advantages of shared components
Because of shared components, the structure of a Fractal component, in terms of direct and indirect sub components, is not necessarily a tree, but can be a directed acyclic graph (it cannot be an arbitrary graph, because a component cannot be added inside itself or inside one of its direct or indirect sub components). In terms of bindings, this structure can be arbitrary, provided it follows the constraints of section 4.1. In particular, bindings can form cycles.
The ContentController interface also specifies two operations to get the internal interfaces of the component, which are similar to the getFcInterface and getFcInterfaces operations. These operations are useful to bind the internal interfaces to sub components.
The content controller operations may throw a NoSuchInterfaceException exception if a specified client interface does not exist, an IllegalLifeCycleException exception when a component is not in an appropriate state to perform an operation and, in case of other errors related to content control, an org.objectweb.fractal.api.control.IllegalContentException exception.
A component interface implementing ContentController (resp. SuperController) must be named content-controller (resp. super-controller).

Note

In order to associate local names to the sub components of a component, similar to the local names of the interfaces of a component, a possibility is to ensure that all these sub components provide the NameController interface defined in Figure 10.
interface NameController {
   string getFcName ();
   void setFcName (string name);
}
Figure 10: Name control API
A component interface implementing NameController must be named name-controller.

4.5  Life cycle control

Changing an attribute or a binding, or removing a sub component, with the above control interfaces, and while components are executing, can be dangerous: messages can be lost, the application's state may become inconsistent, or the application may simply crash. In order to provide a minimal support to help implement such dynamic reconfigurations, a component can provide the LifeCycleController interface (see Figure 11).
package org.objectweb.fractal.api.control;
interface LifeCycleController {
   string getFcState ();
   void startFc () throws IllegalLifeCycleException;
   void stopFc () throws IllegalLifeCycleException;
}
Figure 11: Life cycle control API
This interface provides two operations named startFc and stopFc, to start and stop a component properly. As for the addFcSubComponent and removeFcSubComponent operations, the semantics of these operations is voluntarily as weak as possible, so that many implementations are possible: these operations may or may not be recursive, i.e. starting or stopping a component may or may not automatically start or stop all its direct and indirect sub components. Likewise, the effect of these operations on the component's state is voluntarily not specified (in fact it cannot be specified here, because the APIs defined in this document do not provide access to this state). In particular, the stopFc operation can be seen as a clean up operation invoked before the component is destroyed, or as a suspend operation. In the first case the component's state will be erased, while in the second case it will be left unchanged.
In addition to these operations, the LifeCycleController interface also provides a getFcState operation. This operation returns the current state of the component (in a strict sense, i.e. without taking into account its sub components, which can have a different execution state), as a string. The STARTED and STOPPED strings mean that the component is started or stopped, respectively.
In the STARTED state, i.e. just after successful completion of a call to startFc, a component can emit or accept operation invocations, which are guaranteed to execute "normally". Note that this does not prevent the unbindFc and removeFcSubComponent operations to throw the IllegalLifeCycleException if they are invoked while the component is in this state (in order to prevent a component from being reconfigured while it is in an unstable state).
In the STOPPED state, i.e. just after successful completion of a call to stopFc, a component cannot emit operation invocations, and can accept operation invocations only through control interfaces. The result of operation invocations to the functional interfaces of a stopped component is undefined. It may be a normal result, an exception, a suspension of the invocation until the component is restarted, or anything else.
The LifeCycleController interface corresponds to a minimal life cycle automaton, whose transitions are represented in the following table:
 STOPPEDSTARTED
startFcSTARTEDSTARTED
stopFcSTOPPEDSTOPPED
However, some components may require very different life cycles. Of course, completely arbitrary life cycles can be specified by providing completely new interfaces, distinct from the LifeCycleController interface. More commonly, life cycles can be adapted from the basic one by extending the LifeCycleController interface to introduce new states and transitions or even to change the transitions of the basic life cycle. In this case, it is a requirement of this specification that the semantics associated to the STARTED and STOPPED states should be preserved.
The org.objectweb.fractal.api.control.IllegalLifeCycleException exception may be thrown when a requested transition, in a life cycle automaton, is not valid.
A component interface implementing LifeCycleController must be named lifecycle-controller.

5  Instantiation

The frameworks presented in the previous sections allows one to use, introspect, configure and reconfigure existing components. In order to be useful, they must be completed with a framework to create new components. This section defines such a framework, based on factories.

5.1  Factories

In the instantiation framework specified in this section, components are created by other components called component factories. The Fractal model distinguishes between generic component factories, which can create several kinds of components, and standard component factories, which can create only one kind of components, all with the same component type. Generic and standard component factories can provide the GenericFactory interface and the Factory interfaces, respectively (see Figure 12).
package org.objectweb.fractal.api.factory;
interface GenericFactory {
   Component newFcInstance (Type t, any controllerDesc, any contentDesc)
      throws InstantiationException;
}
interface Factory {
   Type getFcInstanceType ();
   any getFcControllerDesc ();
   any getFcContentDesc ();
   Component newFcInstance () throws InstantiationException;
}
Figure 12: Instantiation API
The GenericFactory interface provides only one operation named newFcInstance. This operation takes as parameter the type of the component to be created, a descriptor of its controller part, and a descriptor of its content part. This operation creates a component corresponding to the given description, and returns its Component interface.
The Factory interface also provides a newFcInstance operation, but this operation does not take any parameter, which reflects the fact that all the components created by this operation have the same type, and the same controller and content descriptors. These information can be retrieved with the three other operations of this interface, named getFcInstanceType, getFcControllerDesc and getFcContentDesc.
In both interfaces, the component type must describe only the functional interfaces of the components to be created. The control interfaces of the components to be created must indeed be specified in the controller descriptors. The exact semantics of the controller and content descriptors, in both interfaces, is however left unspecified in this version of the Fractal component model specification.
Note that, in both interfaces, the newFcInstance operation does not necessarily create a new component instance each time it is invoked. It can also, for example, always return the same instance (this is the singleton pattern). The components created by a factory must be created in the same address space as the factory component. But the exact location of the created components, in this address space, is voluntarily not specified. In particular, it is not ensured that the components created by a factory are automatically added to the parent component(s) of the factory component.
The org.objectweb.fractal.api.factory.InstantiationException exception must be thrown when a component cannot be created, in the newFcInstance operations of the Factory and GenericFactory interfaces.
A component interface implementing GenericFactory must be named generic-factory. A component interface implementing Factory must be named factory.

5.2  Templates

A template is a special kind of standard factory component that creates components that are quasi "isomorphic" to itself. More precisely, the components created by a template component must have the same functional client and server interfaces as the template component (except for the Factory interface, which is provided by the template, but not necessarily by its instances), but can have arbitrary control interfaces. The components created by a template component also have the same attributes as the template. A template component may contain several sub template components, bound together through bindings. The components created by such a template component are components that contain as many sub components as sub templates in the template, bound together as the sub templates are bound in the template (see Figure 13). If some sub templates are shared, the corresponding sub components in the components created by the template will also be shared.
templates.png
Figure 13: A sample template component and a component created from it
If a generic factory component is able to create template components, then it must be possible to create a template component with a operation invocation, on this generic factory, of the form newFcInstance(type, templateControllerDesc, {controllerDesc, contentDesc}), where type describes the functional client and server interfaces of the components that the template will create, templateControllerDesc is the descriptor of the controller part of the template component to be created, and controllerDesc and contentDesc are the descriptors of the controller and content parts of the components that the template will create (the brackets denote an array).

5.3  Bootstrap

According to the above framework, components are created from component factories. But how are created component factories? They can be created from other component factories, but this leads to an infinite recursion. In order to stop it, a bootstrap component factory, which does not need to be created explicitly, and which is accessible from a "well-known" name, is necessary. This bootstrap component factory must be able to create several kinds of components, including component factories. In other words, it must provide the GenericFactory interface.
In Java, this bootstrap component must be accessible from the getBootstrapComponent static method, defined in the org.objectweb.fractal.api.Fractal class. This method must not take any parameter, and must return the Component interface of the bootstrap component.

6  Typing

This section defines a simple type system for components and component interfaces. This type system reflects the main characteristics of component interfaces, introduced in section 3, i.e. their name, their language type, and their role (client or server). It also introduces two new characteristics named contingency and cardinality.

6.1  Contingency and cardinality

The contingency of an interface indicates if the functionality corresponding to this interface is guaranteed to be available or not, while the component is running:
The cardinality of an interface type T indicates how many interfaces of type T a given component may have:
Mandatory and optional interfaces are useful for components that absolutely require other components to work, and which may also use other components, if they are present. For example, a parser component absolutely needs a lexer component, but can work with or without a logger component. Collection interfaces are useful for components with a variable number of required components of the same type, such as a menu component and its associated menu item components, a model component and its listener components (in the MVC model), and so on.

6.2  Type system

In the type system specified here, a component type is just a set of component interface types. A component type is represented by the ComponentType interface (see Figure 14). This interface defines a getFcInterfaceTypes operation, which returns the set of component interface types in this component type, as an array. It also defines a getFcInterfaceType operation, which returns the component interface type whose name is given as parameter (this operation must throw the NoSuchInterfaceException if the requested interface type does not exist).
A component interface type is represented by the InterfaceType interface. Such a type is made of a name, a signature, a role, a contingency and a cardinality. The name is the name of component interfaces of this type. The signature is the name of the language interface type that is implemented by component interfaces of this type (for a client interface, an empty signature means that this client interface can be connected to any server interface). The role indicates if component interfaces of this type are client or server interfaces. The contingency indicates if the functionality of interfaces of this type is guaranteed to be available or not. Finally, the cardinality indicates how many interfaces of this type a component may have.
Component and component interface types can be created by using a type factory, represented by the TypeFactory interface. Indeed this interface provides two operations to create component interface types and component types. A component interface implementing TypeFactory must be named type-factory.
package org.objectweb.fractal.api.type;
interface ComponentType {
   InterfaceType[] getFcInterfaceTypes ();
   InterfaceType getFcInterfaceType (string itfName) throws NoSuchInterfaceException;
}
interface InterfaceType {
   string getFcItfName ();
   string getFcItfSignature ();
   boolean isFcClientItf ();
   boolean isFcOptionalItf ();
   boolean isFcCollectionItf ();
}
interface TypeFactory {
   InterfaceType createFcItfType (string name, string signature, boolean isClient,
      boolean isOptional, boolean isCollection) throws InstantiationException;
   ComponentType createFcType (InterfaceType[] itfTypes) throws InstantiationException;
}
Figure 14: Typing API
A component of type T must have as many external interfaces as described in T (and, in particular, in the interface cardinalities), and all these interfaces must have the name, language type and role described in the corresponding component interface type. Likewise, if this component also exposes its content, and in particular its internal interfaces, then it must also have, at most, as many internal functional interfaces as described in T, and each of these interfaces must have the name, language type and role described in the corresponding component interface type. This implies that each internal functional interface has a complementary external interface of the same name, signature, contingency and cardinality, and of opposite role (but the converse is not necessarily true).
Note that if, in general, the number of interfaces of a Fractal component may change during its life time, the number of interfaces of a Fractal component that uses the type system presented here cannot change during its lifetime (except for interface collections). Indeed the ComponentType and InterfaceType interfaces do not offer any operations to modify an existing type, and the other interfaces specified in this document do not offer a operation to change the type of a component or of an interface. But a Fractal component may perfectly provide a setFcType operation, if needed, since the Fractal model is extensible.

6.3  Sub typing relation

This section defines a sub typing relation for component and component interface types, based on substitutability. This relation provides a sufficient (but not necessary) condition such that if a component type T1 is a sub type of T2, then a component of type T1 can replace a component of type T2 in any environment, this environment (other components and bindings) being left unchanged, and both components being seen as black boxes.
An interface type I1 is a sub type of a server interface type I2 if the following conditions are satisfied: I1 has the same name and the same role as I2; the language interface corresponding to I1 is a sub interface of the language interface corresponding to I2; if the contingency of I2 is mandatory, then the contingency of I1 is mandatory too; if the cardinality of I2 is collection, then the cardinality of I1 is collection too.
An interface type I1 is a sub type of a client interface type I2 if the following conditions are satisfied: I1 has the same name and the same role as I2; the language interface corresponding to I1 is a super interface of the language interface corresponding to I2; if the contingency of I2 is optional, then the contingency of I1 is optional too; if the cardinality of I2 is collection, then the cardinality of I1 is collection too.
A component type T1 is a sub type of a component type T2 if and only if each client interface type defined in T1 is a sub type of an interface type defined in T2, and each server interface type defined in T2 is a super type of an interface type defined in T1.

7  Options

As said in section 1.2, in the Fractal component model, everything is optional. For example, a Fractal component may provide or not the Component interface, it may support or not the Interface interface, it may provide or not the control interfaces defined in section 4, it may use or not the type system defined in section 6, and so on.
In addition, a Fractal component may provide or use new or alternative control interfaces, type systems, or even component semantics. For example, a Fractal component may provide a new ConcurrencyController interface to control concurrent accesses to the component. It may also provide an alternative BindingController interface, named for example InternalBindingController, to control the bindings between sub components directly from the enclosing component. It can also use an empty type system, with a unique type, sub type of itself, used for all components and component interfaces. A Fractal component may even define a new semantic for the communication between its sub components: instead of specifying that operation invocations follow bindings, as defined in section 4.1, it can for example specify that operation invocations are broadcasted to all the sub components, in order to model an asynchronous, reactive "space". Bindings are then useless. A Fractal component may also refine the internal component structure defined in section 4.1, by specifying that the component's controller can, like the component's content, contain sub components. Such a Fractal component can then provide new control interfaces to introspect and reconfigure the sub components of its controller part.
The advantage of this extreme modularity and extensibility is that the Fractal component model can be applied to many situations. The drawback is that two arbitrary Fractal components will generally not be able to work together, because they will generally use very different, and potentially incompatible, options or extensions of the Fractal model. In order to reduce this problem, this section defines some set of options, and gives them a symbolic name called a conformance level. The goal is to be able to say, or even certify, that a given Fractal application or tool is conform to the Fractal model of level X. It will then be easy to know which Fractal applications and tools can work together, by comparing their conformance level to the Fractal model.

7.1  Conformance levels

This specification defines the following conformance levels (new conformance levels can of course be defined as needed):
CICT, ITAC, BC, CC, LCFT
0      
0.1   x  
1x     
1.1x  x  
2xx    
2.1xx x  
3xxx   
3.1xxxx  
3.2xxxxx 
3.3xxxxxx
Figure 15: Conformance levels to the Fractal component model
These conformance levels can summarized as shown in Figure 15, where C, I, CT, IT, AC, BC, CC, LC, F and T represent the component, interface, component type, interface type, attribute controller, binding controller, life cycle controller, factory and "template" interfaces, respectively, and where an x denotes a mandatory feature.
Note that a level 3 component is also a level 2, 1 or 0 component, a level 2 component is also a level 1 or 0 component, a level 3.3 component is also a level 3.2, 3.1 or 3 component, but a level 3, 2 or 1 component is not a level 0.1, 1.1 or 2.1 component. More generally, if l1 is greater than l2 in alphabetical order, a level l2 component is not necessarily also a level l1 component (this desirable rule cannot always be respected, because the alphabetical order is a total order, while the set inclusion order is only a partial order).

Encapsulated components

As specified above, at the 3.2 level, the bootstrap generic factory must be able to create 3.2 primitive components that encapsulate 0.1 level components. These encapsulating components (which do not have a ContentController interface, and therefore are not composite components) must "delegate" all the operation invocations they receive on their functional and control interfaces to their encapsulated component, if the encapsulated component has a corresponding interface. For example, if the bindFc or startFc operation is invoked on an encapsulating component, this component must in turn invoke this operation on its encapsulated component, if it provides the BindingController or LifeCycleController interface. The encapsulating component can of course do some pre and post computations before and after calling its encapsulated component.
If the encapsulated component provides a BindingController interface, then the encapsulating component must invoke, during its initialization, the bindFc operation of this interface with, as arguments, the component name and the reference of its Component interface, so that the encapsulated component can get a reference to the Component interface of its encapsulating component.

7.2  Extensions

The above conformance levels may not be sufficient to fully compare two Fractal systems. In particular, for Fractal systems dedicated to a specific language, such as C or Java, the language is as important as the conformance level. And, even for Fractal systems based on the same language, other "details" (such as, in Java, the class loading policy - one class loader per component vs a single class loader for all the components), unspecified here, may cause incompatibilities. However, if needed, these other features can be captured in new conformance levels, such as C.x.y or J.x.y, for C and Java respectively.

8  Example

This section shows how a 3.3 level, Java Fractal platform can be used, in order to illustrate how the APIs defined in this specification can be used to create, assemble and reconfigure component configurations.
The example used throughout this section is a very simple application made of two primitive components inside a composite component (see Figure 16). The first primitive component is a "server" component that provides an interface s of type S. The other primitive component is a "client" component, bound to the previous server interface.
example.png
Figure 16: A sample component based application

8.1  Instantiation

The above components can be instantiated as follows. The first step is to create the component and component interface types. In order to do this, we get a reference to the bootstrap component, and then to its TypeFactory interface:
Component boot = Fractal.getBootstrapComponent();
TypeFactory tf = (TypeFactory)boot.getFcInterface("type-factory");
We can now create the types of the root, client and server components as follows:
ComponentType rType = tf.createFcType(new InterfaceType[] {
   tf.createFcItfType("m", "M", false, false, false)
});
ComponentType cType = tf.createFcType(new InterfaceType[] {
   tf.createFcItfType("m", "M", false, false, false),
   tf.createFcItfType("s", "S", true, false, false)
});
ComponentType sType = tf.createFcType(new InterfaceType[] {
   tf.createFcItfType("s", "S", false, false, false)
});
We could now create the components directly, but we will use intermediate template components here, in order to show how they can be used. These component templates can be created as follows:
GenericFactory gf = (GenericFactory)boot.getFcInterface("generic-factory");

Component rTmpl = gf.newFcInstance(
   rType, "compositeTemplate", new Object[] {"composite", null});
Component cTmpl = gf.newFcInstance(
   cType, "template", new Object[] {"primitive", "CImpl"});
Component sTmpl = gf.newFcInstance(
   sType, "template", new Object[] {"primitive", "SImpl"});
Here the template (resp. compositeTemplate) descriptor is supposed to describe components with a BindingController interface (resp. with a BindingController and a ContentController interfaces). The primitive and composite descriptors are supposed to describe similar components, but with an additional LifeCycleController interface. Finally, CImpl and SImpl are the names of the Java classes of the 0.1 level Fractal components that will be encapsulated in the client and server components (see end of section 7.1). The CImpl class, for example, has the following form:
public class CImpl implements M, BindingController {
   private S s;
   public String[] listFc () { return new String[] { "s" }; }
   public Object lookupFc (String name) {
      if (name.equals("s")) return s;
      return null;
   }
   public Object bindFc (String name, Object itf) {
      if (name.equals("s")) s = (S)itf;
   }
   public Object unbindFc (String name) {
      if (name.equals("s")) s = null;
   }
   // ...
}
We can then either instantiate each template one by one, put the resulting primitive components inside the composite component, connect all these components, and finally start them. But we can also put the primitive templates inside the composite template, connect these templates together, and then instantiate the whole application by just instantiating the composite template component. This is what we do here.
We begin by putting the primitive template components inside the composite one (here we assume a strong semantic for the addFcSubComponent method, i.e. we assume that C¢=CÈ{ c} - see section 4.4):
ContentController cc = (ContentController)rTmpl.getFcInterface("content-controller");
cc.addFcSubComponent(cTmpl);
cc.addFcSubComponent(sTmpl);
We then bind the internal client interface m of the composite template to the server interface m of the client template:
((BindingController)rTmpl.getFcInterface("binding-controller"))
   .bindFc("m", cTmpl.getFcInterface("m"));
Finally, we bind the client interface s of the client template to the server interface s of the server template:
((BindingController)cTmpl.getFcInterface("binding-controller"))
   .bindFc("s", sTmpl.getFcInterface("s"));
At this stage the template components are like the components depicted in Figure 16, with just an additional Factory interface. Now that the template components have been created and bound to each other, the application components can be instantiated and bound to each other automatically, by just calling the newFcInstance method on the root template component:
Component r = ((Factory)rTmpl.getFcInterface("factory")).newFcInstance();

The result is depicted in Figure 17. As can be seen, the 0.1 level components CImpl and SImpl have been encapsulated in 3.2 level components, which provide them component and interface introspection functions.

example2.png

Figure 17: Result of the instantiation of the application depicted in Figure 16

All the application components can now be started automatically by just calling the startFc method on the root application component (here we assume a stronger semantic than the default one for the startFc method, i.e. we assume it to be recursive - see section 4.5):
((LifeCycleController)r.getFcInterface("lifecycle-controller")).startFc();

8.2  Reconfiguration

Let us suppose we want to dynamically change the server component. In order to do this, we need to unbind the client component, remove the server component, create a new server component, add the server component in the composite component, and finally bind the client component to the new server. But the binding and component removals cannot be done while the client and the composite component, respectively, are not stopped. So we must first stop these components (here again we assume this method to be recursive; we also assume that it does not change the states of the components, and that method calls to functional interfaces while the components are stopped are only suspended until the components are restarted):
((LifeCycleController)r.getFcInterface("lifecycle-controller")).stopFc();

We then retrieve the references of the client and server components (more precisely of the 3.2 level components that encapsulate the 0.1 level components CImpl and SImpl):
Component c = ((Interface)((BindingController)r.
   getFcInterface("binding-controller")).lookupFc("m")).getFcItfOwner();
Component s = ((Interface)((BindingController)c.
   getFcInterface("binding-controller")).lookupFc("s")).getFcItfOwner();
We can now unbind the client and server components, and remove the server component from the composite component (we assume a strong semantic for removeFcSubComponent):
((BindingController)c.getFcInterface("binding-controller")).unbindFc("s");
((ContentController)r.getFcInterface("content-controller")).removeFcSubComponent(s);
We can now create the new server component, i.e. a new 3.2 level component encapsulating a new 0.1 level component. Instead of using a template component for doing that, as in the previous section, we use here the bootstrap generic factory directly:
Component newS = gf.newFcInstance(sType, "primitive", "NewSImpl");
We can now add this new component in the composite component, bind it to the client component, and finally resume the application's execution (we make the same semantic hypotheses as in the previous section for the addFcSubComponent and startFc methods):
((ContentController)r.getFcInterface("content-controller")).addFcSubComponent(newS);
((BindingController)c.getFcInterface("binding-controller")).bindFc("s", newS);
((LifeCycleController)r.getFcInterface("lifecycle-controller")).startFc();

A  Mappings

This section defines some mappings from the Fractal IDL to some programming languages. True interoperability between Fractal components implemented in different programming languages is not a goal of these mappings, in this version of the specification.

Java mapping

Since the Fractal IDL is a modified subset of Java, the Java mapping is very simple to define. Indeed, the Java interface corresponding to a Fractal IDL interface can be obtained with the following transformations:
In addition, the exceptions defined in the operation signatures must be mapped to concrete Java exceptions classes. A Fractal IDL exception named foo.BarException must be mapped to the following Java class:
package foo;
public class BarException extends Exception {
   public BarException (String msg) { super(msg); }
}
The java.lang.RuntimeException and java.lang.Error exception classes, and their sub classes, correspond to the UnknowException Fractal IDL exception.

C mapping

This section defines a possible mapping from the Fractal IDL to C. Other mappings are possible, and can be used instead of this one, depending on the requirements of the targeted applications.
TODO.

B  Glossary

This section defines the core concepts of the Fractal model.
binder: a naming context that can also give access (reference) to the interfaces designated by the names it manages.
binding: a primitive, local communication path between a client and a server interface. More complex "bindings" are made of bindings and of binding components.
concept: an abstract representation composed of the properties common to a set of concrete representations of directly observable entities.
cardinality: a property of an interface type that indicates how many interfaces of this type a given component may have. The cardinality is either singleton or collection.
component: a runtime entity exhibiting a recursive structure and reflexive capabilities. A component is composed of a controller and a content. A component has well defined access points called interfaces, and provides more or less introspection and control capabilities (intercession) to other components.
conformance level : a symbolic name that designates a set of options or extensions of the Fractal component model. A Fractal system is conform to a given conformance level if it supports all the options designated by this level.
content: one of the two parts of a component, the other one being its controller. A content is an abstract entity controlled by a controller. The content of a component is (recursively) made of sub components and bindings.
contingency: a property of an interface indicates if the functionality of this interface is guaranteed to be available or not, while its component is running. The contingency is either optional or mandatory.
controller: one of the two parts of a component, the other one being its content. A controller is an abstract entity that embodies the control behavior associated with a particular component. A controller can exercise an arbitrary control over the content of the component it is part of (intercept incoming and outgoing operation invocations for instance).
entity: anything having existence.
factory: a component that can create other components. Generic factories can create several kinds of components, while standard component factories create only one kind of components.
fractal: a property that characterizes entities (objects in nature; sets, functions in mathematics; software components in the case of Fractal) which exhibit a structure at all scales or at least at numerous scales, i.e. whose structure depends explicitly on the resolution at which they are being observed. Some fractal systems are scale invariants - which means they exhibit in fact the same structure at all scales. Fractal software systems are scale invariant. They are modelled as interacting Fractal components which are self similar: they are not identical (of course!) but exhibit the same structure expressed in terms of interfaces, bindings, attributes and controllers at any resolution they are being observed.
intercession: the ability of a component (seen as a program) to modify its own execution state; or to alter its own interpretation or semantics.
interface: an access point to a component, also called a component interface; or a language interface, i.e. a type made of several operation declarations.
introspection: the ability of a component (seen as a program) to observe and reason about its own execution state.
model: a system of relations between selected concepts - built explicitly at ends of description, explanation or forecast.
name: a value that designates a component interface, but that does not necessarily give access to (reference) it.
naming context: a entity that creates and manages names. Naming contexts can be nested and overlapping, allowing names to be valid in different naming contexts. A component controller constitutes a primitive naming context.
reflection (reflective capabilities): the ability of a component (seen as a program) to manipulate as data the entities that represent its execution state during its own execution. This manipulation can take two forms: introspection and intercession.
role: a property of a component interface, indicates if this interface is a client or server interface.
signature: of a component interface, is the name of the language interface type corresponding to this component interface.
template: a special kind of factory that creates components that are "isomorphic" to itself.
type: a set of structural properties common to a set of entities (components and interfaces for instance).

C  Change History

Changes from version 1.0

The document has been completely rewritten. It is now focused exclusively on the Fractal component model, i.e. the considerations about the Fractal framework and the associated roles have been removed. All features of the Fractal model have been made optional, so that the model can be used in many situations, from embedded software to application servers. Finally the Fractal model itself has been revised, in order to clarify and/or simplify some points (binding control, interface collections...).
Changes in the document:
Major changes in the API:

Changes from version 0.9

Changes in the document:
Major changes in the API:
Minor changes in the API:

Changes from version 0.8-0

Changes in the document:
Major changes in the API:
Minor changes in the API:

Changes from version 0.7.3-0

Changes in the document:
Major changes in the API:
Minor changes in the API: