A Refactoring-based Tool for Software Component Adaptation Gautier Bastide Ecole de Mines de Douai 941 rue Charles Bourseul 59508 Douai, France bastide@ensm-douai.fr Abstract Adapting software components usable by a particular application is a crucial issue in software component based technology. In fact, software components can be used in contexts that can be different from the context assumptions made by the component designers. We present in this paper a tool aiming at adapting software component structure. Among the motivations of this kind of adaptation, we note its possible application to prepare a flexible deployment of software components according to the available resources (CPU, memory). Our adaptation process is based on refactoring and fragmentation of component source code. To support this structural adaptation technique, we developed an adaptation process which we have experimented using the Java framework of the Fractal component model. 1 Introduction Component-Based Software Engineering (CBSE) aims at building applications by assembling reusable components [12]. However, experiments show that direct component reuse is extremely hard and a component usually has to be adapted in order to be integrated into an application. This difficulty is due to the available large variety of infrastructures and software environments, going from the simple mobile phone equipped with minimal capacities to the cluster of several multi-processor computers. Moreover, the development of networks and wireless technology imposes the application designers to take into account all characteristics of the available infrastructure like the type and the size of the available hosts (i.e. how many hosts are available and what are the properties of each one in term of resources as memory or energy) and the Quality of Service considered for this infrastructure, for example, in terms of load balancing, performance and security policy allowed on each host. However, in many cases, software components in general and COTS (i.e. Commercial-Off-The-Shelf) in particular are designed as a monolithic unit to be deployed in a centralized way. Furthermore, in other cases, when components are designed to be distributed, the deployment infrastructure configuration is specified since the design time. Thus, it is not possible to specify at deployment time what services are to be deployed and executed on a particular host. To illustrate this, let us consider an example of a monolithic COTS shared-diary component which provides shareddiary services (e.g. personal diary management, meeting management and absence management). The application administrator may want to distribute some component services on various hosts. For example, services related to database management may be deployed on a secure server whereas other services may be transferred onto different sites in order to increase performances (i.e. parallelism). Due to the monolithic implementation of this component it is not possible to obtain the needed deployment configuration of the component services. While being based on the above considerations, our objective in this paper is to propose a tool allowing the software component restructuring. In fact, our approach permits to restructure existent software component implementations using slicing and refactoring techniques. We focus on components based on an object-oriented implementation and we experiment this approach using Fractal component model [3] and its Java implementation named Julia [2]. We discuss the proposed approach in the rest of this paper as follows. Section 2 presents the general mean of the structural adaptation of a software component. Section 3 describes the adaptation process. In section 4, some related works are briefly mentioned. Finally, conclusion and future works are outlined in section 5. 2 Software component refactoring Software component refactoring modifies the structure of a component while preserving its behaviour and its services. In fact, the structural adaptation is interested in modifying neither the behaviour of the component nor its services, but Proceedings of the Conference on Software Maintenance and Reengineering (CSMR’06) 0-7695-2536-9/06 $20.00 © 2006 IEEE rather to reorganize its structure. Indeed, the structure of the component which has to be adapted consists of the list of the structural elements, defined in this component, as well as their links. For example, a component can be seen as collections of ports, interfaces, sub-components, classes, etc. Thus, the structural adaptation can consist of updating the list of a component interfaces by modifying the content of each of these interfaces or restructuring the content of the component ports (i.e. external adaptation). Also, the structural adaptation can consist in the decomposition of a software component in some sub-components where the set of provided services must be the same as the one provided by the initial component before its adaptation (i.e. internal adaptation). Adapting the structure of a software component can be useful in many cases. For example, external structural adaptation can be used in order to increase component reuse and internal structural adaptation for a flexible component deployment. First, component restructuring can be used in order to match heterogeneous component external structures. In fact, the assembly of two components or the integration of a component into an existing application needs to restructure their provided and required interfaces (or/and ports) if it proposes all required services but when these ones are not structured in interfaces permitting to match them. For example, Figure 1 shows that the C1 and C2 components cannot be assembled because C1 required ports do not match with those provided by C2 although C2 provides all required services. So, using our approach for restructuring ports, matching between C1 and C2 becomes possible. Moreover, the component structure adaptation by its fragmentation in some generated components can be used to prepare a flexible deployment strategy. Indeed, this adaptation permits to organize component services in separate sub-sets and defining each sub-set in a new component generated by fragmenting the original one. Then, the generated components can be deployed on one machine or separately on different ones which can be distributed or not. These possibilities are very useful for load balancing and increased performance (Figure 1). Figure 1. Structural adaptation 3 Software component restructuring process As we announced previously, the restructuring of a component aims at generating new components by the fragmentation of a component based on an object-oriented implementation. This operation is realized without generating any change on the services provided by the component nor on its assembly with the other components of the application. To resolve these constraints, we defined a restructuring process made up of four stages (Figure 2). Figure 2. Structural adaptation process 3.1 This first step aims at indicating which components are generated as a result of the component fragmentation. This specification is realized using an ADL (Architecture Description Language) defining components to be generated and for each component its interfaces. We consider that initial interfaces are unbroken units1 . Each interface defines a sub-set from services provided by the initial component. 3.2 IEEE Component fragmentation and generation of the needed structures Specification done during the previous step is used in order to generate the implementation of each new component created by adaptation. In fact, the component fragmentation aims at splitting component service collection and consequently the corresponding component internal implementation in some sub-collections of services where each subcollection will be provided by a corresponding component. The definition of these components requires the generation of their external implementation in term of interfaces and services and the corresponding object-oriented internal implementation. Two conditions need be checked: the first condition is the construction of each new component so as to guarantee its integrity. In fact, the generated components must function correctly without syntactic and semantic bugs. The second condition consists in ensuring the coherence of the new generated component. We proposed 1 In order to simplify, we consider interfaces like unbreakable entities. So, specification and restructuring may be realized at the provided service scale. Proceedings of the Conference on Software Maintenance and Reengineering (CSMR’06) 0-7695-2536-9/06 $20.00 © 2006 Specification of the new structure to guarantee this condition by operating at the componentinterface scale and at the object-implementation scale. In order to ensure the first condition, we propose a bottomup construction of components to be generated. Starting from ports and interfaces specified as provided by a component to be generated, we form, by analyzing component source-code, a graph which we called SBDG (Structural and Behavioural Dependence Graph). Nodes of the SBDG are structural elements which must be accessible from the component space-name and arcs are dependence links between these structural elements. Dependence links are of two types: structural and behavioural links. For example, a port P1 composed of an interface I1 is structurally dependent of this last. However, if a method M1 calls a method M2 then M1 depends on M2 through a behavioural link. 3.3 Assembly of the new components Once the new components are generated, they have to be assembled. This assembly reflects two types of dependence links between these components. Behavioural dependences: The first type of dependence relates to the behavioural dependence links between these components. A behavioural link reflects the use, by a generated component, of a given service defined by another generated component. Behavioural dependences are set up through the implementation of services provided by specific interfaces added to the generated component structures: • A required interface implemented for each component and which include all needed services implemented by other components. • A provided interface implementing all services implemented by this component and required by other components. Resource sharing dependences: The second type of link reflects the resource sharing between two generated components. A resource is defined as being a structural entity which has a state that can be updated and whose persistence is more important than that of the method in which it is used. A resource can be internal (e.g. attributes, etc.) or external (i.e. persistent elements as files and databases). As we proposed to duplicate internal structural entities, we need to ensure coherence and synchronisation of the resource states. The shared-resource dependences are translated in the form of interfaces added to the structure of each component. Connection of these interfaces allows the sharing and the communication of a common state of the sharedresources. To materialize shared-resource dependences, we defined, for each generated component defining a sharedresource, two interfaces: • The first interface is defined as required and synchronous. It allows the component to notify to all components sharing a resource with it, its state modification. This interface is synchronous because the component which updates the resource state can continue its execution only after that the other components which share this resource take into account this state modification. It allows to guarantee that components have a coherent state and consequently their services. • The second interface, defined as provided, allows a component to receive the notifications of sharedresource updates. In addition to the notification problem, we need to ensure that shared resources cannot be handled simultaneously. To prohibit simultaneous handling of these resources, we defined two additional interfaces: • The first interface, defined as required and synchronous, allows it to get a resource access right. This interface guarantees that only one access to the resource is possible for the given time. • The second interface defined as provided and synchronous allows a component to notify the availability of a resource to requester components. 3.4 The last step of the adaptation process is the integration, in the subjacent application, of the structural adaptation result, which was obtained during the previous steps. It consists in connecting the new created components with the other application components and to guarantee that the component adaptation is achieved in a transparent way compared to the application components. In fact, the application must continue to be executed without any change compared to its initial configuration (i.e. configuration including the component before its adaptation). For that, it is necessary to satisfy security and flexibility properties. Concerning security conditions, application components should not be able to access, after the adaptation, other services than those which are provided by the component before its adaptation. This means that all new interfaces defined to guarantee the new generated components must be accessible only by these components. The other components of the application will not be able to use services available through these interfaces. For example, it would be forbidden to be able to access, by the application components except those created by adaptation, to the state of a shared resource. The flexibility condition means that the new generated components can be accessed and be handled as separate entities, Proceedings of the Conference on Software Maintenance and Reengineering (CSMR’06) 0-7695-2536-9/06 $20.00 © 2006 IEEE Integration of the adaptation result one of the others. For example, it would be possible to deploy separately these sub-components. Our solution to guarantee these properties consists in encapsulating the new structure resulting from the adaptation in a new composite-component. This new component allows to mask access to ”non functional” services. It wraps all the generated components and thus filters all accesses to them. This composite-component provides only interfaces which were available by the adapted component before its adaptation. Moreover, it provides some interfaces which allow to manipulate the generated components. For example, these interfaces aim at permitting independent deployment of each sub-component. 4 Related works We classify related works according to two criteria. First, we present works related to software component adaptation. The other criterion focuses on works whose topic is restructuring program codes. If we consider the first criterion related to the goal, many adaptation approaches have been discussed in the literature. Broadly speaking, adaptation techniques can be categorized as either white-box or black-box [7]. White-box techniques typically require understanding of the internal implementation of the reused component, whereas black-box techniques only require knowledge about the component’s interfaces. To our knowledge, no approach among those quoted, is interested in the adaptation of component structures. All are interested in the adaptation of services. This adaptation can be carried out in a static [8] or dynamic [4], [9] way. Concerning the second criterion related to restructuring approaches, we can quote refactoring techniques [10], [11] whose aim is to restructure an existing body of an objectoriented code, altering its internal structure without changing its external behaviour [10]. Generally, refactoring is used to make code simpler in order to include or understand it more easily [5]. Another technique of program analysis is slicing [13]. It is generally used for the code debugging and testing [1], for maintaining [6] or for transforming source code. 5 Conclusion and future works We presented an approach to deal with the adaptation of software components. This approach is based on the consideration of a new adaptation facet: the structure of a software component. This approach has been implemented. The prototype has been developed using the Julia [2] software component framework which is a Java implementation of the Fractal component model [3]. We have developed, in addition to the structural adaptation process presented here, a component model supporting this process. This model was not presented here due to space limitation. Our approach needs source code and it does not consider run-time adaptation problems. However, it is generic enough to be applicable for a static or dynamic adaptation. Nevertheless, concerning the dynamic adaptation, it is necessary to define, in addition to the already defined process, mechanisms for the management of the dynamicity (e.g. disconnection, service recovery, etc.). Thus, the dynamicity management constitutes one direction of our future work. As we explain, many applications of the structural adaptation approach are possible. Thus, for example, we want to develop our approach for context-awareness system (i.e. ubiquitous applications) in order to load or unload component services according to the available resources. References [1] H. Agrawal, R. A. DeMillo, and E. H. Spafford. Debugging with dynamic slicing and backtracking. Software - Practice and Experience, 23(6):589–616, 1993. [2] E. Bruneton. Julia tutorial: http://fractal.objectweb.org/tutorials/julia/. [3] E. Bruneton, T. Coupaye, and J. Stefani. Recursive and dynamic software composition with sharing. In Proceedings of the 7th ECOOP International Workshop on ComponentOriented Programming (WCOP’02), Malaga, Spain, 2002. [4] P.-C. David and T. Ledoux. Dynamic adaptability of services in enterprise javabeans architecture. In Proceedings of ECOOP’02 Workshop on Component-Oriented Programming, Malaga, Spain, 2002. [5] M. Fowler, K. Beck, J. Brant, W. Opdyke, and D. Roberts. Refactoring: Improving the Design of Existing Code. Addison-Wesley Object Technology Series, 1999. [6] K. B. Gallagher and J. R. Lyle. Using program slicing in software maintenance. IEEE Transactions on Software Engineering, 17(8):751–761, 1991. [7] G. Heineman and H. Ohlenbusch. An evaluation of component adaptation techniques. Technical Report WPI-CS-TR98-20, Department of Computer Science, Worcester Polytechnic Institute, February 1999. [8] R. Keller and U. Hölzle. Binary component adaptation. Lecture Notes in Computer Science, 1445:307–??, 1998. [9] A. Ketfi, N. Belkhatir, and P. Cunin. Automatic adaptation of component-based software: Issues and experiences. In PDPTA’02, 2002. [10] T. Mens and T. Tourwe. A survey of software refactoring. In IEEE Transactions on Software Engineering, volume V. 30 of 2, pages 126–139, 2004. [11] W. F. Opdyke. Refactoring Object-Oriented Frameworks. PhD thesis, Urbana-Champaign, IL, USA, 1992. [12] C. Szyperski. Component software: beyond object-oriented programming. ACM Press/Addison-Wesley Publishing Co., New York, NY, USA, 1998. [13] M. Weiser. Program slicing. In Proceedings of the 5th international conference on Software engineering, pages 439– 449, Piscataway, NJ, USA, 1981. IEEE Press. Proceedings of the Conference on Software Maintenance and Reengineering (CSMR’06) 0-7695-2536-9/06 $20.00 © 2006 IEEE