Abderrahmane Seriai
email: abderrahmane.seriai@irisa.fr

Salah Sadou
email: salah.sadou@irisa.fr

Houari A Sahraoui
email: sahraouh@iro.umontreal.ca

Enactment of Components Extracted from an Object-Oriented Application

come   L'archive ouverte pluridisciplinaire

Introduction

Most existing works on extraction of components from a legacy system have as a main aim the construction of an understandable architecture [START_REF] Medvidovic | Using software evolution to focus architectural recovery[END_REF][START_REF] Washizaki | A technique for automatic component extraction from object-oriented programs by refactoring[END_REF][START_REF] Allier | Restructuring objectoriented applications into component-oriented applications by using consistency with execution traces[END_REF]. When the legacy system is implemented in the object-oriented paradigm, a component is represented by a cluster of classes with a set of provided methods and a set of required methods. Thus, the identification of the components consists in finding the groups of classes that are the most cohesive and loosely coupled. So, the obtained results have the advantage to offer a more abstract representation via a component-oriented architecture view of the object-oriented application.

The extracted software architecture facilitates the understanding of the legacy system. However it needs to be complemented by a mapping between architectural elements and their corresponding ones in the code in order to facilitate the achievement of maintenance. In fact, sets of classes, representing components, can not be easily projected onto a specific component model [START_REF] Allier | Restructuring objectoriented applications into component-oriented applications by using consistency with execution traces[END_REF]. This problem is due to the shift from the concept of object instances to the concept of component instances. Indeed, it is not easy to infer a component instances from a set of class instances. Hence, the executable version of the application remains in its old form and therefore has no direct correspondence with the architecture. Consequently, there is no direct mapping between the architecture and the running application.

To solve this problem, we need to be able to project the extracted components on a concrete component model. This will give the advantage of creating a direct mapping between architectural elements and their equivalents in the code of the application. To achieve this purpose, we need to (i) identify the interfaces of the extracted components to make them consistent with the component paradigm concepts, and (ii) determine how the concerned classes will be instantiated with respect to component instances. This second concern, which is neglected in literature on component extraction, is important as it allows to formalize the notion of component instance, which is necessary to make the application executable and at the same time its components reusable by others applications.

In a recent work [START_REF] Seriai | Deriving component interfaces after a restructuring of a legacy system[END_REF], we proposed a solution for the point (i) based on a static analysis of the extracted components. In this paper, we propose a solution for the point (ii). Our approach considers that the extraction of components (cohesive groups of classes) is already performed. It is based on the hypothesis that an instance of a component consists of a connected set of instances of its classes. Thus, the objective is to identify all instances of classes representing an instance of a component in order to build the component's factory. This will provide the necessary means for the framework to run the restructured application. Furthermore, the identification of the component instances allows us to propose a dynamic approach to the identification of the component interfaces.

To demonstrate the feasibility of our approach, we present its implementation within the Spring component framework. After that, we apply it on a Java application that we restructured into a component-based application with the approach presented in [START_REF] Allier | Restructuring objectoriented applications into component-oriented applications by using consistency with execution traces[END_REF]. To validate the correction of the restructuring, we have replayed the application's case studies on its component-based version and the results were identical to those of its object-oriented version.

The rest of the paper is organized as follows: in the next section we describe the process of our approach. Sections 3 shows how to define instances of a component starting from the objects of its classes. Then, the definition of the component's interfaces and the creation of its instances is described in Section 4. In Scetion 5 we show how our approach can be implemented using the Spring component framework. Before concluding, we present the related work in Section 6.

Approach

The group of classes, which represents a component, are part of the definition of the component descriptor. The descriptor of a component is equivalent to the class in the object-oriented paradigm. Thus, what is lacking with the group of classes is the way to build instances of the component. Indeed, to create a component-based application, as in the case of an object-oriented application, it requires creating component instances and binding them.

For this work, we propose the following definition for a component instance: Definition 1 : An instance of a component consists of all instances of its classes, which have had connections during the execution of the application and thereby forming a connected group.

Objects surrounded by a dashed line in figure 3 is an example of component instance. To build component instances, we must first identify the instances of classes that compose them and their links. This is why our approach, as shown in Figure 1, begins with the execution of the application's use cases in order to extract traces of method calls between objects. This information will be summarized in an object call graph (step 1 of the process).

The use of the application's use cases is a way to get only objects that actually play a role in the functionalities provided by the system. Thus, all the other objects from classes held by the application are naturally avoided and have no chance to infer in the proposed process.

By analysing all objects, instances of classes belonging to the same component, we can find several connected groups. It is these groups of objects that represent instances of the component. Thus, we can reduce the obtained object call graph to a component call graph in order to focus on the relationships between component instances (step 2 of the process).

The identification of component instances is interesting only to deduce a way to build them. For a given component, some of its instances may have similarities when considering the type of their involved objects. Thus, these component instances suggest a common constructor. Indeed, these component instances have a similar configuration of their constituent objects that we define as follows:

Definition 2 : Two instances of the same component belong to the same configuration if and only if their subsets of objects, which are directly concerned by the component's incoming calls, are similar. Definition 3 : Two sets of objects are similar if and only if they contain the same number of class instances for each involved class.

Finding all possible instance configurations for each component is the goal of the step 3 of our process. Once the possible instance configurations of a component are identified, we need to define a constructor for each of them. Subsequently, to each configuration of instances, we associate a component provided interface (goal of the step 4). Thus, with our approach, each component interface highlights one of its aspect, which is emerged by the configuration. For a component, its required interfaces will be defined according to the identified provided interfaces of all components on which it depends.

In the following sections, we describe each step of our approach.

3 From Object to Component Call Graph An important step in our approach is to identify component instances, and their bindings, by considering the classes they hold. The component instances will consist of objects from its classes. For this aim, we first construct an object call graph in order to transform it into a component instance call graph.

Object Call Graph

The first step of our approach consists of identifying all possible class instances for the entire application and build their links. This leads to the construction of a call graph specific to class instances (objects).

To get this call graph, we run the application with all its use cases to capture the execution traces. An execution trace corresponds to a directed tree T (V, E) where V is a set of nodes and E a set of edges between nodes. Each node V i represents an instance of the class (Cl i ). An edge V i , V j indicates that an instance i calls a method of an instance j. The root of T (V, E) corresponds to the entry point of the system.

As shown in Figure 2(left), the nodes of the tree are labeled by the identifier, the actual types of the objects that are called and the concerned methods. As the execution traces are based on method calls, it is possible to have nodes containing the same object (same identifier) with calls on different methods. This is the case for object d0 which appears twice for two different methods (see left part of Figure 2). So, these nodes are grouped in the same one in order to get a graph where each object is represented by exactly one node. Thus, the resulting node contains all called methods. An example of such a transformation is given in the right part of Figure 2. The resulting graph corresponds to what we call Object Call Graph (OCG).

Component Call Graph

The identification of component instances is based on our definition of component instance (see Section 2). Thus, starting from the OCG of an application, we need to identify the sub-OCG that may be associated with each component. Recall that our working hypothesis is that for each component, we know the classes composing it. Thus, finding the sub-OCG associated with a component leads to find the sub-OCG composed of all objects that are associated with the component.

Figure 3 (left side) shows examples of such a sub-OCG. Objects associated with the same component are marked with the same symbol (circle, triangle or square). For instance, the dimmed objects are associated with the same extracted component (triangle symbol), which holds the classes A, B, C, D, E and J. When an object is marked with several symbols, it means that it is used inside several components and thus, its class is used to define these components. This situation arises when components exchange object references through service calls. We will discuss the responsibility of creating this kind of objects in the next section.

By analyzing the sub-OCG of a component, we can identify sub-graphs. These subgraphs correspond to possible instances of the component. Figure 3 (right side) provides a representation of the OCG that is reduced to component instances. That is what we call Component Call Graph (CCG). Thus, in a CCG, nodes are instances of components, and edges correspond to calls between components. In other words, edges correspond to calls between objects belonging to different components. For example, instances of the component represented by the dimmed sub-OCG in the right part of nents. To each method name mthi, are associated the full method signature and the class to which it belongs.

In the case of an object that is shared by several component instances, all calls to (or from) this object, and coming from (or to) other objects held by these components, are considered internal and therefore, not visible at the CCG level. For example, in the OCG of Figure 3, the object j0 belongs to two component instances of different types (square and triangle). Thus, the call from c1 to j0 is considered internal to the component instance of triangle type and the call from f1 to j0 is considered internal to the component instance of square type. This is why in the corresponding CCG (right part of the figure) there is no edge between the component instances Comp2.1 and Comp1.1.

Interface Identification

By analyzing the instances of a component, we can identify similar configurations of objects they contain. According to the definition given in Section 2, component's configurations help in defining its provided interfaces. At the same time, a configuration of a component reflects one of its aspects. Thus, we can also use the configuration to define a constructor for the component.

In the following, we describe our approach to identify configurations of a component as well as the constructors associated with them.

Configuration Identification

In Figure 3, the two instances Comp1.0 and Comp1.1 of component COMP1 have in common the fact that their accessible objects from outside (other components) are of the same type (b0 and b1 of type B and shown in bold). Thus, these component instances are associated with the same configuration. This configuration is characterized by the fact of exposing an object of type B as an interface to other components. The two component instances, which have given rise to this configuration, show that only the methods mth1, mth2, mth14, and mth15 of the class B are used by the other components. Moreover, one of the two instances the component (Comp1.0) requires an instance of another component (Comp2.0). As the latter belongs to a given configuration, so we can link the dependency to this configuration.

Thus, we define a configuration as a triple (ObjInt, MethInt, ReqConf) where:

ObjInt corresponds to a set of objects belonging to component instances of the configuration, which are called by other component instances. MethInt corresponds to the union of sets of methods from component instances of the configuration that other component instances use. ReqConf is the set of configurations of component instances that are required by those of the current configuration.

For instance, the configurations of the component given in the example above correspond to the following triplets:

({b0:B,b1:B},{mth1,mth2,mth14,mth15},{configuration1 of COMP2}) and ({c0:C,c1:C},{mth6,mth7,mth8,mth9},{configuration1 of COMP2})

From a configuration of a component, we can deduce one of its provided interfaces and some of its required interfaces. Indeed, the list of methods associated with the configuration correspond to a provided interface of the component. Thus, each provided interface is associated with one and only one configuration of the component. Furthermore, as the configuration requires configurations from other components, the provided interfaces associated with those configurations define the required interfaces of the targeted component.

Thus, the provided interfaces of a component correspond to the set of provided interfaces suggested by its configurations. And its required interfaces correspond to the union of the provided interfaces associated with the configurations required by its configurations. From the example given above, we deduce the following required and provided interfaces:

Provided interfaces = {{mth1,mth2,mth14,mth15}, {mth6,mth7,mth8,mth9}} Required interfaces = {{mth3,mth4}}.

From the list of objects held by a configuration, we can also define the necessary constructors for the component instances associated with this configuration. We will show that in the next sub-section.

Component Constructors

With the notion of configuration, we have grouped a set of component instances around the same provided interface. Although these instances are used through the same types of objects, the way to create them is not necessarily the same.

Indeed, each object can have different constructors that can be used independently to create component instances associated with the same configuration. Thus, we must Figure 4 shows a component (Component1) that requires an interface of another component (Component2). This interface concerns only objects of types A and B. When tracing the different use cases, we distinguish three instances of component Component2 that are associated with this interface (see the right part of the figure). We note that the objects concerned by the interface were created by using different combinations of their constructors.

The different combinations of object constructors are grouped as a Factory method pattern. This pattern allows the construction of the various component instances that are associated with the same configuration (required interface). Thus, each provided interface of a component is associated with a Factory method pattern for the construction of component instances to be used through this interface.

Calls to object constructors of a component instance are actually dispersed in the component that uses this instance. Recall that each component instance is associated with a given provided interface. Thus, a component that uses a component instance will require an interface of the same type to which the component instance is associated with. Therefore, any references to objects of the component instance that is in the user component, must be transformed into a single reference to the component instance.

In the left part of Figure 4, component instances of Component1 use component instances of Component2 through object references (aAtt and bAtt) of type A and B. The type of these references must be replaced by the type of the required interface (Interf1). Moreover, these references must be initialized with the same component instance. This implies that each component instance holds its own identification, which will be communicated to all the objects that constitute it. This is equivalent to the this attribute in the object paradigm. As shown in Figure 4, the component's classes will be changed in order to add a reference to the component instance as an attribute and a parameter in their constructors for initializing this attribute. The propagation of the identifier of a component instance to all objects that constitute it will be initiated by its associated Factory method pattern. Objects shared by different component instances (necessarily from different components) will receive the identifier of the component instance that created them.

Thus, calls to constructors of objects belonging to the required component instance will be replaced by a request of required component instance from the component instance to which the object belongs. As shown in the class F of Figure 4, bAtt = new (var1, var2) is replaced by bAtt = compInst.getRef1Interf1(). This implies that in the component descriptor, there is a getter method for each required interface.

The call to the Factory method of the provided interface of a component can be set in the component that requires this interface as it can be placed outside all components and thus constitute the configuration file of the application. The choice of the concrete implementation of the Factory method pattern depends on the targeted component framework. In the following section, we give a solution within the Spring component framework.

Case Study

The objective of our approach is to make components extracted from an object-oriented application projectable on a concrete component model. Thus, we chose the case of Spring as a concrete model and framework. Our approach relies on the existence of extracted components represented by sets of classes. In the past we had done this work on a concrete application called Logo.

Below we give a brief description of the Logo application, followed by the tools developed for the implementation of our approach and we conclude by showing how the components are projected onto Spring by using the Logo application as example.

Logo Application

The Logo application consists in a language for learning programming and its interpreter. The latter has a graphical interface which allows writing the code and a window, which shows the result of this code graphically. This system was selected for two reasons: (i) its reasonable size allows us to perform a deep analysis of the results. (ii) we already extracted its components. (iii) one of its developers was available to comment the results.

The component-based architecture of the Logo interpreter, which was extracted thanks to the approach proposed in [START_REF] Allier | Restructuring objectoriented applications into component-oriented applications by using consistency with execution traces[END_REF], contains four components:

-The Language Parser component is used to read the logo code, to interpret it according to the Logo grammar, and to launch appropriate java treatments. -The Evaluator that receives a list of instructions and evaluates them one after another in the current lexical environment. -The Graphical Display component displays the results of a Logo program that makes the connection between the Logo code, its evaluation, and its visual results. -The Graphical User Interface (GUI) component represents the graphical interface through which beginner programmers interact with the application.

The components above consist of sets of classes.

Process and Tools

We defined a tool for each step of our process (see Figure 1). All the tools were implemented in Java using JVMTI 3 . These tools are as follow:

Tracer This tool allows the generation of execution traces (instances creation, method calls, attribute access, etc). This was made using a custom extraction agent written in C that utilizes the JVMTI API. This agent crops at each entrance or exit into/from a method, the relevant information, such as the class and the instance where the method is executed, the current thread, etc. ObjectCallGraphBuilder Using the traces provided by the Tracer, this tool constructs an object call graph. ComponentConfigurationBuilder Using information about contained classes for each component and the object, this tool uses algorithms from graph theory to generate connected sub-graph for each component (its different instances). It also provides the component's configurations. ComponentInterfacesExtractor This tool analyses dependencies between the objects involved in a configuration and those from the other components in order to define: (i) the provided interface associated with the configuration. (ii) the components that require this interface. (iii) the constructors for the component instances associated with the interface.

ComponentToSpring This tool generates the classes representing component instances according to the Spring framework. It also modify classes of a component in order to make their objects aware about the component instance they constitute. Finally, it produces the configuration file for the application.

The first step of our experiment consists in executing scenarios corresponding to the 15 identified Logo use cases. Examples of such use cases are "file creation/saving", "code writing in the editor", "code interpretation", etc. Thanks to the Tracer, events that occur in the Logo application during the execution of these use cases are collected. Each event indicates which object calls which other object and on which method. Since we are interested only in events involving classes of the Logo application, we filtered all the noises produced by the agent tracer. Indeed, the used extraction agent is listening to all events at each entrance or exit of methods, even those that come from libraries and mouse/ keyboard events, etc.

After that, the ObjectCallGraphBuilder, and the ComponentConfigurationBuilder are executed to build the component instance configurations for each component. After the identifying component interfaces thanks to the ComponentInterfaceExtracor, the ComponentToSpring tool produces the necessary classes to make the component-based version of the application according to the Spring framework.

Bellow, we detail how these classes are generated.

Generated code for Spring

As shown in Figure 5, each component is represented by an abstract class. All its configurations correspond to concrete classes of the abstract class that represents it. The interface associated with a configuration of a component is represented by a Java interface as shown in the example below. Thus, each configuration implements its corresponding interface. <!--Definition for EvaluatorComp-instance2 bean --> <bean id="EvaluatorConf2" class= "com.irisa.evaluatorcomp.ConfEvaluationHandling"> <constructor-arg ref="ParserConf1"/> </bean> <!--Definition for ParserComp-instance1 bean --> <bean id="ParserConf1" class="com.irisa.parsercomp.ConfErrorHandling"> <constructor-arg ref="GuiConf2"/> </bean> <!--Definition for GuiComp-instance2 bean --> <bean id="GuiConf2" class="com.irisa.guicomp.ConfEventsHandling"> ...

</bean>

For the first created component instance (EvaluatorConf2) we can notice that the reference on the component instance (ParserConf1) is injected via the constructor of the component. This will be used for the required interface of the component instance (EvaluatorConf2).

The main statements in the launcher of the Logo application in its Sprint version are the follow:

IEventsHandling mainApp = (ConfEventHandling)
context.getBean("GuiConf2"); mainApp.main(args);

The first statement allows to retrieve an instance of the EventHandling component and to use it through its IEventsHandling interface. This component contains the class that holds the launcher (main method) of the Logo application, which is provided through the IEventsHandling interface. Thus, the second instruction starts the application.

To validate the component-based version of the Logo application, we replayed the 15 use cases, which were used to extract execution traces, and we got the same results. After that, we checked that the generated components can be used independently from each other. We reused the EvaluatorComp component in an application that allows to test the validity of Logo expressions through a command line. So we built a fairly simple component that allows to enter a Logo expression through the standard input. It requires the IEvaluationHandling interface of the EvaluatorComp component. It uses mainly the evalList method to submit the proposed expression. The returned result is translated into an understandable message and then printed on the standard output.

Obviously, we used an instance of ParserComp component that is required by the EvaluatorComp component. Apart from this component instance, which is perfectly appropriate, the reuse of the component do not generate any problem.

Related Work

The reverse engineering research community has been actively investigating techniques to decompose (partition) the structure of software systems into subsystems (clusters or component). In this section we target only work concerning the recovery of components in a legacy system.

Architecture Extraction

Software architecture plays an important role in at least six aspects of software development: understanding, reuse, construction, evolution, analysis and management [START_REF] Garlan | Software architecture: a roadmap[END_REF]. Many approaches and techniques were proposed in the literature to support software architecture recovery [START_REF] Martin | Deriving high-level abstractions from legacy software using example-driven clustering[END_REF][START_REF] Pourhaji Kazem | An evolutionary approach for partitioning weighted module dependency graphs[END_REF][START_REF] Saeed | Software clustering techniques and the use of combined algorithm[END_REF][START_REF] Mancoridis | Using automatic clustering to produce highlevel system organizations of source code[END_REF][START_REF] Yan | Discotect: A system for discovering architectures from running systems[END_REF][START_REF] Richner | Recovering high-level views of object-oriented applications from static and dynamic information[END_REF][START_REF] Riva | Combining static and dynamic views for architecture reconstruction[END_REF], and often the problem is seen as a software clustering problem. The software clustering problem consists of finding a good partition of software modules based on various criteria, in particular, the dependencies among these modules [START_REF] Martin | Deriving high-level abstractions from legacy software using example-driven clustering[END_REF]. Dependencies are extracted by static analysis, dynamic analysis, or using a combination of both (so-called hybrid approaches).

Among the approaches that use static analysis, Pourhaji Kazem et al. [START_REF] Pourhaji Kazem | An evolutionary approach for partitioning weighted module dependency graphs[END_REF] proposed a genetic algorithm for clustering based on the weighted module dependency graph. Saeed et al. [START_REF] Saeed | Software clustering techniques and the use of combined algorithm[END_REF] used the Rigi tool to extract the function dependency graph and presented a new clustering algorithm called the "combined" algorithm to implement software architecture recovery. Mancoridis et al. [START_REF] Mancoridis | Using automatic clustering to produce highlevel system organizations of source code[END_REF] extracted the file dependency graph from the source code and used a clustering algorithm based on a genetic algorithm.

With regard to approaches that use dynamic analysis, Yan et al. [START_REF] Yan | Discotect: A system for discovering architectures from running systems[END_REF] described a technique that uses run time observations about an executing system to construct an architectural view of the system. In a previous work, we proposed an approach to restructure an object-oriented application into a component-oriented one [START_REF] Allier | Restructuring objectoriented applications into component-oriented applications by using consistency with execution traces[END_REF]. This approach is based on dynamic calls, i.e. actual calls at runtime with use cases, to determine the dependencies between classes. These dependencies are then used by a genetic algorithm to derive groups of classes representing components.

For hybrid approaches, Richner et al. [START_REF] Richner | Recovering high-level views of object-oriented applications from static and dynamic information[END_REF] presented an environment supporting the generation of tailorable views of object-oriented systems from both static and dynamic information. Claudio Riva et al. [START_REF] Riva | Combining static and dynamic views for architecture reconstruction[END_REF] proposed a technique for combining the analysis of static and dynamic architectural information to support the task of architecture reconstruction.

All these work achieve the starting point of the approach proposed in this paper (ie, sets of classes representing components). Thus, these work are complementary to our approach.

Component Instance Identification

In the field of Component-oriented programming (COP) , where the components are created from scratch (bottom-up approach) [START_REF] Fabresse | Foundations of a simple and unified component-oriented language[END_REF], a component instance is uniquely identified with regard to the other instances, and is obtained from a component class (component descriptor), to enable use of the features associated with the component during the execution time. A variety of component-oriented languages have been proposed in the literature [START_REF] Fabresse | Foundations of a simple and unified component-oriented language[END_REF][START_REF] Bruneton | An open component model and its support in Java[END_REF][START_REF] Peter | Supporting software composition at the programming language level[END_REF][START_REF] Zenger | Keris: evolving software with extensible modules[END_REF] to define components ( component classes andor component instances). These component languages are either dedicated to only software specification and are not executable (eg. UML 2.0 [START_REF]Unified modeling language 2.1.2 super-structure specification[END_REF]) or dedicated as well as to transform mod-els [START_REF] Fabresse | Foundations of a simple and unified component-oriented language[END_REF][START_REF] Costa | A basic model of typed components[END_REF] into executable codes or to write programs by hand. SCL [START_REF] Fabresse | Foundations of a simple and unified component-oriented language[END_REF] is an example of the latter case, which defines the component by a descriptor that can be instantiated.

Regarding the field of restructuring object-oriented systems into component-based systems, to the best of our knowledge, there is no work that identifies instances of extracted components.

Conclusion

The work presented in this paper aims to complete work on the extraction of components from legacy systems. Indeed, our approach allows to completely restructure an object-oriented application into a component-based application. Thus, it makes permanent mappings between elements from the extracted architecture and their corresponding ones in the code of the application. Identifying the different instances of a component highlights its various aspects. Defining the interfaces of a component based on the various configurations of its instances is a way to make it reusable according to its different aspects.

Thus, we performed this work as a continuation of the work we have already done on the extraction of components from an object-oriented application [START_REF] Allier | Restructuring objectoriented applications into component-oriented applications by using consistency with execution traces[END_REF]. Given the assumption we made (ie, the components are represented as a set of classes), the proposed approach also applies to all work on the extraction of components from object-oriented applications. However, our approach requires the existence of use cases in order to identify instances of components.

We have shown that instances of a component can be used to define its interfaces. We have already proposed an approach for the identification of interfaces of a component through a static analysis (on source code) of its dependencies on other components [START_REF] Seriai | Deriving component interfaces after a restructuring of a legacy system[END_REF]. We used the same application as a case study (Logo) and we found some differences in the identified interfaces. In fact, static analysis takes into account objects that may be created but do not really exist in the context of the application (polymorphism). On the other side, dynamic analysis allows to get objects related to classes dynamically loaded. But the obtained interfaces are related to the context of the concerned application.

In one of our old work we presented an approach for component extraction that relies on a combination of static analysis (on source code) and dynamic analysis (calls between objects) [START_REF] Allier | From object-oriented applications to component-oriented applications via component-oriented architecture[END_REF]. This combination of the two approaches of analysis allowed to better cover aspects of extracted components. We think this may be the case with the definition of component interfaces. Thus, we expect in a future work the definition of component interfaces based on a combination of the two types of analysis in order to deduce dependencies between instances of components and getting more reusable components.

Fig. 1 .

 1 Fig. 1. Process of the proposed approach.

Fig. 2 .

 2 Fig. 2. Example of execution trace tree (left) and its corresponding object call graph (right).

Figure 3 Fig. 3 .

 33 Fig. 3. (a) Example of sub-OCG, (b) Example of component call graph.

Fig. 4 .

 4 Fig. 4. Example of transformation of object constructors into a Factory method pattern.

Fig. 5 .

 5 Fig. 5. Class diagram representing a component within the Spring framework.

  public interface IEvaluationHandling { public void initEnv(HashMap<String, Object> penv); public Object evalList(ArrayList<Object> listInstruction); } Below you have an excerpt of the configuration file for the Logo application in its component-based version.

Java Virtual Machine Tool Interface (JVMTI) API is a tool that provides both a way to inspect the state and to control the execution of applications running in the Java virtual machine (VM) (http://docs.oracle.com/javase/6/docs/technotes/guides/jvmti)

Both interface method above come from two different classes. These are the classes of the objects involved in the configuration associated with the interface. This interface is implemented by the class that represents its configuration. The code below gives a brief description of such a class. This class inherits from the abstract class representing the component. This is the way to associate a configuration to a component. The first attribute corresponds to the required interface. It will be injected via the constructor of the class using the configuration file (see below for an example). The two other attributes correspond to the objects that are directly involved in the configuration. The Factory method ObjectFactory creates the objects associated with the component instances. Note the "this" given to constructors of the objects that allows them to know the component instance to which they are associated. Methods of the interface are implemented as redirections to the corresponding objects.


Abderrahmane Seriai
email: abderrahmane.seriai@univ-ubs.fr

Salah Sadou
email: salah.sadou@irisa.fr

Houari Sahraoui
email: sahraouh@iro.umontreal.ca

Salma Hamza
email: salma.hamza@irisa.fr

Deriving Component Interfaces after a Restructuring of a Legacy System

Keywords: Object-Oriented Application, Component-Based Architecture, Interfaces Identification, FCA (Formal Concept Analysis)

come   L'archive ouverte pluridisciplinaire

I. INTRODUCTION

Nowadays, components are most often built using objectoriented technology. This is normal as, usually, new programming paradigms are defined using previous ones. That was the case for classes that are built on the procedural paradigm.

Classes are designed to hide their implementation (procedural details), i.e., encapsulation principle. This is also true for components that need to hide their implementation (details of contained classes). Components must expose only their provided and required interfaces. The organization of these interfaces should not reflect the implementation details of their component. They must be organized according to the component's business logic independently of classes that actually provide services exposed by the interfaces. This problem appears when building components from scratch. However, it is more blatant during automatic restructuring of an object-oriented application into a componentbased application. Indeed, in this case the approach of extraction of components is mainly based on the consistency of the groups of classes. In addition, the obtained components often have a large number of classes, which complicates the identification of their interfaces. There are two advantages of this type of restructuring: i) allowing a better understanding of the system to achieve its current maintenance thanks to its extracted architectural representation based on the component paradigm. ii) facilitating its future maintenance by its reimplementation within a component technology while relying on its architectural representation. Most of the work on the extraction of components from object-oriented applications consider components as clusters of classes with a set of provided methods and a set of required methods [START_REF] Kim | A systematic method to identify software components[END_REF], [START_REF] Lee | Component identification method with coupling and cohesion[END_REF], [START_REF] Medvidovic | Using software evolution to focus architectural recovery[END_REF], [START_REF] Washizaki | A technique for automatic component extraction from object-oriented programs by refactoring[END_REF].

To the best of our knowledge, one of the most important works that projects the extracted components on a concrete component model used an extracted component oriented architecture [START_REF] Allier | From object-oriented applications to component-oriented applications via component-oriented architecture[END_REF]. In this work, the organization of the interfaces was achieved through a direct mapping to the component's classes. Although this projection is an improvement with respect to the existing work, it still reveals the details of the component implementation. The disadvantage is that if the definition of component interfaces is strongly influenced by structural aspects, coming from the object approach (component's internal classes), it complicates the understanding of the relationships between components. Indeed, component interfaces should represent its functional aspects and its relationships with the other components should imply only that.

In this paper, we propose to consider the problem of organizing the interfaces of a component starting from its classes and all their exposed methods. Thus, our starting point is the result obtained by the majority of the works on component extraction from object-oriented applications. More concretely, we view interface organization as a clustering problem based on dependencies between the exposed methods (services) and components that use them. Formal-concept analysis techniques are used to perform the clustering. The idea behind this is that when a component uses another, it means that it needs one or more specific aspects from the latter. The analysis of all interactions of a component allows us to define its various exposed aspects. Each aspect is represented by a subset of the exposed methods and will be implemented as an interface of the component. This applies to the provided interfaces. Similarly, in a context where components are extracted from the same application, the required interfaces of a component are defined according to interfaces provided by the other components that it uses.

The rest of the paper is organized as follows: Section II in-troduces some definitions, highlights the component extraction issues, the interfaces identification and gives an overview of Formal Concept Analysis (FCA). The interfaces identification approach is presented and illustrated with an example in section III. Our approach is evaluated and discussed through an empirical study in Section IV. Concluding remarks are given in Section VI.

II. BACKGROUND AND RELATED WORK

The goal of our work is to organize the interfaces of the extracted components, from an object oriented application, according to their interactions. The perspective is to produce an architecture with more meaningful bindings, which simplifies the system understanding. This step is very important for the projection of the produced architecture into a concrete component model while avoiding object oriented design influence. This section gives the necessary background, including the related work, to the presentation of our contribution.

A. Architecture Extraction

Software architecture plays an important role in at least six aspects of software development: understanding, reuse, construction, evolution, analysis and management [START_REF] Garlan | Software architecture: a roadmap[END_REF]. Many approaches and techniques were proposed in the literature to support software architecture recovery [START_REF] Kim | A systematic method to identify software components[END_REF], [START_REF] Lee | Component identification method with coupling and cohesion[END_REF], [START_REF] Medvidovic | Using software evolution to focus architectural recovery[END_REF], [START_REF] Washizaki | A technique for automatic component extraction from object-oriented programs by refactoring[END_REF], [START_REF] Ducasse | Software architecture reconstruction: A process-oriented taxonomy[END_REF], and often the problem is redefined as a software clustering problem. The software clustering problem consists of finding a good partition of software modules based on various criteria, in particular, the dependencies among these modules [START_REF] Martin | Deriving high-level abstractions from legacy software using example-driven clustering[END_REF]. Dependencies are extracted by static analysis, dynamic analysis, or using a combination of both (so-called hybrid approaches).

Among the approaches that use static analysis, Pourhaji Kazem et al. [START_REF] Pourhaji Kazem | An evolutionary approach for partitioning weighted module dependency graphs[END_REF] proposed a genetic algorithm for clustering based on weighted module dependency graph. Saeed et al. [START_REF] Saeed | Software clustering techniques and the use of combined algorithm[END_REF] used the Rigi tool to extract the function dependency graph and presented a new clustering algorithm called the "combined" algorithm to implement software architecture recovery. Mancoridis et al. [START_REF] Mancoridis | Using automatic clustering to produce high-level system organizations of source code[END_REF] extracted the file dependency graph from the source code and used a clustering algorithm based on a genetic algorithm.

With regard to approaches that use dynamic analysis, Yan et al. [START_REF] Yan | Discotect: A system for discovering architectures from running systems[END_REF] described a technique that uses run time observations about an executing system to construct an architectural view of the system. In a previous work, we proposed an approach to restructure an object-oriented application into a componentoriented one [START_REF] Allier | Restructuring object-oriented applications into component-oriented applications by using consistency with execution traces[END_REF]. This approach is based on dynamic calls, i.e. actual calls at runtime with use cases, to determine the dependencies between classes. These dependencies are then used by a genetic algorithm to derive groups of classes representing components For hybrid approaches, Richner et al. [START_REF] Richner | Recovering high-level views of objectoriented applications from static and dynamic information[END_REF] presented an environment supporting the generation of tailorable views of object-oriented systems from both static and dynamic information. Claudio Riva et al. [START_REF] Riva | Combining static and dynamic views for architecture reconstruction[END_REF] proposed a technique for combining the analysis of static and dynamic architectural information to support the task of architecture reconstruction

B. Interface Identification

We find that the reverse engineering research community has been actively investigating techniques to decompose (partition) the structure of software systems into subsystems (clusters) [START_REF] Schwanke | Using neural networks to modularize software[END_REF], [START_REF] Choi | Extracting and restructuring the design of large systems[END_REF], [START_REF] Muller | A reverseengineering approach to subsystem structure identification[END_REF], [START_REF] Lindig | Assessing modular structure of legacy code based on mathematical concept analysis[END_REF], [START_REF] Van Deursen | Identifying objects using cluster and concept analysis[END_REF], [START_REF] Anquetil | Recovering software architecture from the names of source files[END_REF], but regrettably the most part of researchers limited themselves to that, ignoring the problem of provided/required interfaces identification. In the context of restructuring an application, required interfaces of a component, i.e., what the environment should provide in terms of services to the considered component, are defined so that they match the provided interfaces by the used components, i.e., what the component should provide in terms of services to its clients. For this reason, the first step to perform is to define the provided interfaces for all components.

Among the few studies that have addressed the problem of interface identification, we can cite the work by Chouambe et al. [START_REF] Chouambe | Reverse engineering software-models of component-based systems[END_REF]. The starting point in their approach is a list of classes and interfaces (in the sense of OO programming) and the relationships between each other in the analysed software system. They create two lists L 1 and L 2 of tuples consisting each of a class and an interface. Tuples of L 1 consist of the implementation relationship between classes and interfaces, whereas tuples of L 2 represent the used-by relationship between classes and interfaces. Thereafter, they consider classes as components and the interfaces implemented in the analysed system as the provided interfaces of the extracted components and the interfaces used as the required ones. Although the idea is interesting, it is completely based on the OO application logic. Consequently, to implement/package the identified components according to a concrete component model, one must first identify the provided interfaces and then implement them in the application language.

A trivial solution would be to create a unique interface offering all the exposed methods (services). This solution is, however, undesirable since it requires the other components to use a single interface regardless of the services they need, which implies that components provide only one aspect, which is unlikely. An alternative solution was proposed in [START_REF] Allier | From object-oriented applications to component-oriented applications via component-oriented architecture[END_REF]. In this work, exposed methods are grouped by their classes, i.e., one interface per contained class, if it holds methods used by the other components. Figures 1 shows the result of this strategy for an extracted component.

Although this solution offers more interfaces to be used by the other components, it violates some component oriented principles. Indeed, the interface definition is based on the internal structure of the components. Thus, it violates the encapsulation principle (black box) of the component paradigm. To be consistent with this paradigm, the organization of interfaces should be based on the functional aspect of the component and not on its concrete implementation.

C. Formal Concept Analysis (FCA)

We consider interface organization as a clustering problem. In this perspective, we use formal concept analysis (FCA) [START_REF] Ganter | Formal concept analysis -mathematical foundations[END_REF] to group exposed methods into interfaces. FCA is a classification technique that is based on the lattice theory.

Much works have already used formal FCA for software clustering problems. For example, in legacy system reengineering, FCA was used to identify objects in procedural code [START_REF] Sahraoui | Applying concept formation methods to object identification in procedural code[END_REF], [START_REF] Van Deursen | Identifying objects using cluster and concept analysis[END_REF]. FCA was also used for restructuring existing OO code [START_REF] Dekel | Revealing class structure with concept lattices[END_REF], [START_REF] Snelting | Reengineering class hierarchies using concept analysis[END_REF], refactoring existing code toward aspectoriented [START_REF] Tonella | Migrating interface implementation to aspects[END_REF], [START_REF] Van Deursen | Aspect mining and refactoring[END_REF] or component-oriented [START_REF] Lundberg | Architecture recovery by semi-automatic component identification[END_REF], [START_REF] Allier | Identifying components in object-oriented programs using dynamic analysis and clustering[END_REF] paradigms.

FCA allows the identification of groups of elements having common attributes. FCA considers contexts defined with the triple (O, At, As). O is the set of elements (in our case the calling components), At the set of attributes (in our case the called methods) and As the relation that associates elements to attributes. As is generally represented by an association matrix, with the elements as rows and attributes as columns. An example of a formal context is shown in Table I, with a set of elements O = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} and a set of attributes At = {a, b, c, d, e}. In this table, element 1, for example, has the attributes a and e.

a b c d e 1 × × 2 × × 3 × × 4 × × × 5 × × 6 × × 7 × × 8 × × 9 × × × 10 × ×
FCA extracts the set of all formal concepts from a formal For the example of Table I, co 1 = ({4, 6, 8, 10}, {b, d}) is a formal concept because the objects 4, 6, 8, and 10 share exactly the attributes b and d, and conversely. On the contrary, ({6}, {b, d}) is not a formal concept as it is subsumed by co 1 (subset of the extent and the same intent). In addition to the set of all formal concepts, it is possible to define an inheritance relationship between concepts. The concept co 1 is more general than the concept co 2 if the extent of co 2 is a subset of the one of co 1 , and the intent of co 2 is a superset of the one of co 1 . Formal concepts and their relationships form a concept lattice. The concept lattice obtained from the context of Table I is illustrated in Figure 2 (left).

In Figure 2 (right), we can see that the concept co 2 = ({4}{b, d, e}) is a sub-concept of concept co 1 = ({4, 6, 8, 10}, {b, d}) because it inherits the attributes b and d and adds the attribute e.

III. INTERFACE IDENTIFICATION APPROACH

Conceptually, as depicted in Figure 3, a component is intended to implement a high-level feature (e.g., image processing). This feature is generally offered through a set of facets (e.g., create images, convert images, merge and overlay images, etc). For efficient use of a component and to give meaning to its bindings in an architecture, the various aspects of a component must be represented by separate interfaces. When components are derived from an OO application, each component correspond to a set of classes. The services offered by components are the exposed methods of their classes. However, the notion of interface is not explicitly defined. To create interfaces, one should identify component facets from the code.

Fig. 3. Aspects involved in interface definition

To identify the various functional facets of a component, we rely on the way the other components use it. Indeed, for a given component, the subset of features, which are used by another component, partially highlights one or more of its functional aspects. The analysis of all the facets of a component, which are highlighted by its use by all the other components of the application, allows us to identify a coherent set of interfaces (facets) that the component should expose. Thus, we can treat the problem of interface organization as a clustering problem based on dependencies between components.

A dependency is defined by the pair (calling component, called service). Using this definition of dependencies, we use a formal concept analysis to classify services according to the components that use them.

Figure 4 gives an overview of the proposed approach for the identification of the component's interfaces. The identification is the result of four steps: the first three steps are performed automatically while the last one needs a collaboration with the designer.

In the following sections, we detail the steps of our approach through an illustrative example. This example concerns the Logo application, which was already used to illustrate our approach for component extraction [START_REF] Allier | Restructuring object-oriented applications into component-oriented applications by using consistency with execution traces[END_REF]. The identified components for Logo are as follow:

• Component 1: The Language Parser component is used to read the logo code, to interpret it according to the Logo grammar, and to launch appropriate java treatments.

• Component 2: The Evaluator that receives a list of instructions to evaluate it in the current lexical environment.

• Component 3: The Graphical Display component displays the results of a Logo program.

• Component 4 The Graphical User Interface (GUI) component represents the graphical interface through which beginner programmers interact with the application.

As can be understood through the definition of the above components, the Logo application is a programming learning tool with a graphical display of results.

A. Formal Context Construction and Classification

For each component we build a formal context. This context summarizes the dependencies of the other components with the current one. In our approach, we consider that these dependencies already exist and have been determined during the component extraction process. In the case of redefining The component's methods are referred in the table by their number and the correspondence is given in figure 5. Thus, this component is used by only two components (Graphical Display and Evaluator components). In fact, the GUI component never directly accesses to the Language Parser component.

For each formal context corresponding to a called component, the FCA classifier module derives a concept lattice in which services are grouped according to the calling components. Lattice construction follows the principles described in Section II-C.

The lattices corresponding to the matrix of Table IIis shown in Figure 5.

B. Lattice Interpretation

The aim of the Lattice Interpreter stage is to suggest interfaces for the called component based on its corresponding lattice. Some of concepts may be considered as interfaces thanks to their use by a single component (lower level of the lattice) while others are consequence of a combination of components (upper levels of the lattice).

In the example of Figure 5, three interfaces may be taken into account for the Language Parser component. This may seem obvious because the Logo example was deliberately chosen for its small number of components in order to simplify the explanation of our approach. But when the application contains a large number of components, which is for example the case of applications taken for the empirical study (see Section IV), the number of concepts (interfaces) in a lattice can be very large. So, it is not reasonable to let the designer solely responsible for determining the optimal combination of concepts.

Thus, the Interpretation stage aims to offer a combination of concepts that satisfies the following properties:

1) It must contain all the services exposed by the component. 2) Each service belongs to only one concept: this is dictated by the clustering problem, but it means that a method of a class can participate in only one aspect of the component that contains it. 3) the number of concepts should not exceed a limit set by the designer. Indeed, the number of interfaces per component is one of the quality properties that each company defines according to its experience. 4) Among the combinations of concepts which satisfy the above conditions, the proposed solution must optimize the objective function defined below.

N otU sedBy(m, C) = 0 if m is used by C 1 otherwise (4)
The objective function measures the quality of the selected combination of concepts with respect to aspects. In our approach, the aspects of a component are determined by the components that use it. Thus, the optimum would be to have an aspect (interface) per user component. As the objective function measures the deviation, then the best solution is the one that gives the minimum result. The deviation for a component corresponds to an average of the deviations of all its interfaces (equ. 1). The deviation for an interface is the average of its deviations with all components that use it (equ. 2). The deviation of an interface with respect of a user component is the portion of its methods that are not used by the latter (equ. 3).

Having the lattice corresponding to a component and the objective function, it becomes possible to propose the combination of concepts (component's interfaces) that meets all the conditions mentioned above. The selection of the concepts representing the solution is done through the following steps:

1) In the first step are selected only concepts that are associated with user components. The maximum number of interfaces (rule 3) can be exceeded and the problem will be solved when negotiating with the designer (see next subsection). 2) If the specified number of interfaces is not reached, then the lattice is visited level by level, from the bottom, and are selected concepts that contain the largest number of methods. This process is stopped when the specified number of interfaces is reached. 3) This step is useful only if after reaching the fixed number of interfaces there are still non selected concepts while they contain methods. Each of these concepts will be completely fused with an already selected concept. The aim is to avoid break up an already identified concept.

For the last step, the search for non selected concepts is done by browsing the lattice, level by level, from the top. Thus, we first consider the most constraining concepts. For each, we determine the set of its sub-concepts that are already selected. We consider the fusion of the concerned concept with each concept of that set and we measure the objective function at the interface level (equ. 2). The fusion which obtains the best score will be realized.

Our approach promotes the identification of concepts against a strict compliance with the limitation of the number of interfaces. We believe it is easier to merge identified concepts than identify new ones from existing ones.

C. Refining the Solution

The previous stages are an aid, based on a heuristic approach, to the designer to get a solution which is close to the optimal. Thus, the proposed solution can be considered as the final solution, but it simplifies the work of the designer to achieve the latter. Moreover, it is not possible to automatically find a meaningful name for each interface. At this stage, the designer can refine the proposed solution (eg. moving methods, merging interfaces or splitting an interface), but the most important is giving a meaningful name to each interface. Merging interfaces can result from an exceeding of the maximum number of interfaces that the designer has chosen. In this case, of course, our objective function will be useful to the designer to find the best fusion.

Once the provided interfaces are defined, it is easy to extract the required interfaces. The component's required interfaces are defined such as to correspond to the provided interfaces that it uses. Thus, a component-based architecture of the legacy application can be proposed. For instance, the extracted architecture for the Logo application is shown in Figure 6. As we can see, connections between components contain names with a semantic meaning that simplifies the understanding of the architecture.

IV. EMPIRICAL STUDY

Through this study we want to show the importance of the influence of the object paradigm on the definition of component interfaces when components are built using this paradigm. But we also want to validate that our approach provides consistent interfaces.

To this aim, we selected component-based applications and for each we have achieved the following: First step:

• Extract dependencies between components.

• Based on the extracted dependencies and our objective function, measure the quality of the declared interfaces.

Second step:

• From the already extracted dependencies, apply our approach to propose interfaces to components

• Measure the quality of the proposed interfaces using our objective function

• Take randomly a small number of interfaces to measure the number of changes that the designer may have to make. Thus, in the second step we consider the components as if they were extracted, we deliberately ignore their declared interfaces to propose new ones. Below we explain the most important aspects of this study and we give the results.

A. Selected Applications

For the purposes of the study, we need to reproduce the situation that corresponds to an object-oriented application restructured into component-based application. Thus, the chosen component-based applications must not contain anything other than their specific classes. We want to avoid any interference from classes added by the component framework. To meet these criteria, we decided to select only applications developed within the OSGi component model. Indeed, in OSGi, except business classes, there are no additional classes to define the component. In OSGi, a component is known as a bundle. Each bundle is defined by a single JAR file which packages the module (code and resources) and a manifest file which contains the extra meta-data. In fact, the logical bundle is equated with the physical bundle JAR file (the module) [START_REF] Hall | OSGi in action : creating modular applications in Java[END_REF]. We selected 3 OSGi component-based applications that are all open-source projects and developed in JAVA. We have tried to collect applications having different sizes (number of components in an application and its code size) and belonging to different development teams. Table III provides a short description of these applications.

B. Results

Table IV summarizes the results of our study on the applications when considering as interfaces of a component all interfaces (Java meaning) and public classes that are in its exported packages. The fourth column shows the number of interfaces (as defined above) that are not reported in the exported packages, but are used by other components. This is achieved by breaking the rules that must be observed for developing OSGi components. The number of interfaces that are in this case shows the weakness of the component interface design. The number of the third column also shows this design weakness, since the majority of components are created specifically for their application.

The last column gives the average value of our objective function DevC for all components of the application. For this calculation, we have considered only interfaces that are declared in exported packages. If an interface is declared but not used, it receives 1 for DevC (max value). Thus, the number of declared interfaces but not used (column 3) is heavily involved in the weak score of the average DevC.

Figure 7 shows the results of the objective function DevC for each component from each application. In each graph, at the same time are represented the results related to the interfaces derived with our approach (solid line) and those declared in the manifest file of components (dashed line). We can notice that the results are much better for components with interfaces that were proposed by our approach. In some cases they are not the same set of services which are considered in the calculation of a component DevC. This is the case when some methods of a class are used while the latter does not belong to an exported package.

It is true that the penalty assigned to unused interfaces (1 for DevI) degrades the result of the declared component interfaces. But, at the same time it also shows how the components were poorly designed in terms of defining their

C. Discussion

The obtained results are the same regardless of the application, as shown in Figure 7. Thus, the problem is not related to the context of the application (scope or development team). The common element is the use of object-oriented approach for building components.

The other problem we have noticed is the use of an already existing packages for declaring interfaces. Indeed, with the OSGi framework provided interfaces are declared in a package to export. But programmers use classes that already belong to packages, while the latter are designed for object-oriented design. The logic used to group classes in a single package, according to the object-oriented approach does not correspond to the logical grouping of features according to aspects. This is highlighted by i) the number of interfaces declared by components (in their exported packages) which are not used by the other components. ii) the existence of interfaces that are used but do not belong to exported packages (see Table IV).

The results obtained with our approach depends on the maximum number of interfaces per component set by the designer. Not knowing the semantics of each component of each application, we set this number at 1.5 times greater than the number of user components of each component. There is no doubt that a good knowledge of the components makes it possible to set the limit of the number of interfaces to the correct value and thus get better results.

V. THREATS TO VALIDITY

Our study may be concerned by internal and external threats to validity. We discuss below these two kind of possible threats.

A. Internal Validity

The internal validity is threatened by the chosen tool to extract dependencies between components. We used the JDT Eclipse parser to build the static call graph. Indeed, if dependencies are extracted from a dynamic call graph (actual calls), they may be a little bit different from those obtained from a static call graph. The dynamic call graph may contain calls related to classes dynamically loaded and may miss calls that the used use cases do not cover.

The number calls related to classes dynamically loaded is very small wrt to calls within an application. Thus, they do not impact the results of our study. But, the number of possibly missed calls by use cases may be important. Indeed, it is hard to get use cases covering all possibilities of an application.

That is why we chose the use of a static call graph to extract dependencies between components.

B. External Validity

The external validity is threatened by the language and the framework with which the chosen applications have been developed. We chose applications developed with the Java language. As our study is based on static calls, we do not believe that the results may be different with another objectoriented language. We have chosen the OSGi framework because there are no additional classes that the business classes have to interact with. The use of our approach for redefining interfaces of already existing components, constructed within a framework that adds its own classes (eg. J2EE), can give an inconsistent result. But our approach has been built to be integrated in a process of restructuring of object-oriented applications into componentbased applications. In this case, there are only applicationspecific classes.

VI. CONCLUSION

According to Lehman's first law [START_REF] Lehman | On understanding laws, evolution, and conservation in the large-program life cycle[END_REF], software systems must be continually adapted, or they become progressively less satisfactory. In the same time, the software evolution is becoming more and more complex and expensive. This is explained by the Lehman's second law [START_REF] Lehman | On understanding laws, evolution, and conservation in the large-program life cycle[END_REF] which states that as a software system evolves its complexity increases unless work is done to maintain or reduce it. Given the advantages of the component paradigm, re-engineering object-oriented applications into component-oriented applications seems to be a promising choice. It helps companies reducing their software maintenance and evolution costs. Especially if this process is automated in a large part. In this context, automated identification of components and their interfaces is an important first step. In this paper, we have proposed an interface identification approach that is defined as a continuation of a component identification process. The aim is to build a consistent and understandable architecture that facilitates the maintenance and/or the mapping of the application to a concrete a component model.

Our study of existing applications has shown how the designer is influenced by the object paradigm when designing her/his components. In case of a correctly built application, but mainly with component on the shelf, our objective function can give unsatisfactory results. Indeed, the latter are often generic in order to cover a wide space of application domains. So they can hold aspects that are not relevant to the target application. However, our objective function can be used to choose between competing components to minimize the size of unnecessary code in the application.

To project an architecture, which is extracted with our approach, on an existing component framework, we can use the fac ¸ade and the factory design patterns. For the latter, its use can raise some difficulties. Indeed, the life-cycle of objects from a group of classes (component) is somehow managed by some third parties in the legacy application. To put their lifecycle within the component that holds the concerned classes, we need first to conduct a study on the dynamics of these objects in order to reproduce it within the component. This aspect will constitute our main future work.

Fig. 1 .

 1 Fig. 1. Organization of the interfaces based on contained classes

  context. A formal concept is a maximal set of elements (called extent) sharing a maximal set of attributes (called intent). We can express the formal concept as a pair of corresponding sets (E, I) such that: Extent E = {e ∈ O | ∀i ∈ I, (e, i) ∈ As} (covered objects) and Intent I = {i ∈ At | ∀e ∈ E, (e, i) ∈ As} (shared attributes).

Fig. 2 .

 2 Fig. 2. Example of formal concept lattice (left) and a focus on concept co 2 (right).

Fig. 4 .

 4 Fig. 4. An overview of the component's interface identification approach

Fig. 5 .

 5 Fig. 5. The concept lattices for the Language Parser component

  dev(C) = i∈Ci devI(i) |Ci| with Ci = {interf aces of C} (1) devI(I) = c∈Iu devIbyC(I, c) |Iu| with Iu = {components using I} (2) devIbyC(I, C) = m∈I N otU sedBy(m, C) |I| with I = {methods in the interf ace} (3)

Fig. 6 .

 6 Fig. 6. The extracted component-based architecture for the Logo system.

Fig. 7 .

 7 Fig. 7. Comparative results for DevC before and after the use of our approach

TABLE I .

 I

A FORMAL CONTEXT FOR THE ELEMENTS O AND THE ATTRIBUTES At.

Table

  II shows a part of the formal context associated to the Logo's Language Parser component.

TABLE II

 II

	.	FORMAL CONTEXT FOR THE LANGUAGE PARSER
			COMPONENT
		m1	m2	m3	m4	m5	m6
	C1
	C2	×	×		×	×
	C4		×	×	×		×

TABLE III

 III

		.	SELECTED APPLICATIONS
	Application	Description	Nb. of	Code	version
				compo-	Size
				nents	(KLOC)
	MAT	Eclipse Memory Analyser	13	86	1.2.0
		Tool
	Eclipse E4	Eclipse Platform		28	20	4.0
	IMP	IDE Meta-Tooling Platform	36	117	0.2.1

TABLE IV .

 IV OBTAINED RESULTS BEFORE THE USE OF OUR APPROACH

	Application	# declared	# declared	# used but	Average
		interfaces	but not used	not	DevC
			interfaces	declared
				interfaces
	MAT	481	373	2	0.603
	Eclipse E4	168	161	5	0.573
	IMP	1655	1639	5	0.597