Dynamic Aspect Oriented C++ for Safe Upgrading

0 downloads 0 Views 543KB Size Report
addressed. The paper presents the DAO C++ language - Dynamic Aspect Oriented C++ system, ... applications developed using C++ programming language.
Dynamic Aspect Oriented C++ for Application Upgrading Without Restarting Sufyan Almajali and Tzilla Elrad Illinois Institute of Technology {almasuf,elrad}@iit.edu

Abstract This paper presents an extension to C++ to construct easily upgradeable systems without restarting. With current applications of the Internet, the need for new structures to achieve a high degree of dynamic adaptability becomes apparent. Here we propose to address the issue of modularization of networking services whose implementation would otherwise be scattered in many different places. The advantage of such modularization is that these services become easier to control and adapt at runtime. The cost of extracting what otherwise would be scattered code and modularizing it is the need to provide mechanisms to quantify the points in the program where the code needs to run. In addition, the issue of switching from one system version to another is addressed. The paper presents the DAO C++ language - Dynamic Aspect Oriented C++ system, its implementation and its potential use for dynamic applications upgrading.

1 Introduction Software has moved away from the stand alone applications of pre-Internet days. Client/server applications are the norm and the stand alone applications that exist today are expected to undergo regular upgrades. Software development itself is done in distributed environments. With the universal availability of the Internet we now can alter end-user software, ensuring that each client is using the latest version. Developers can work all across the globe on the same project, or on different projects sharing the same resources. A problem has emerged is that the upgrading process does not always go smoothly. Upgrades can be incompatible, or may introduce subtle errors that don’t appear until later in the program’s life. There is scope for a more rigorous upgrading process [3]. Even if the upgrade itself is fine, the upgrade process frequently causes problems. Usually when a component is replaced, the program (typically a server) needs to be stopped and restarted. The maintainer downloads the new version, tests it, and then if it’s fine, halts the server and installs it. This can cost the application owners and users lost in revenue and customers if their application services are unavailable for any length of time [6]. Because of the need for continuous operation, planned downtime is hard to schedule, and unplanned system halts can cause big lost for these network providers and organizations. When a new service or security threat makes changes unavoidable, the changes are applied quickly with the purpose of minimizing system downtime rather than following software engineering principles. These changes usually reduce modularity and increases coupling. At the same time, ignoring needed maintenance and

1

development causes system to become outdated, eventually to the point that the system becomes an obstacle to organizational development [6]. To avoid this problem, an automatic process of application upgrading is required. Automatic application upgrading can help to achieve dynamic run-time adaptation of services and protocols for many applications. For example, in networking mobile systems, the behavior of a mobile node can change based on the environment that it exists in. Security encryption protocol can change as node move from place to another. Also, a new security code patches and hot fixes can be loaded dynamically at run-time and get integrated with the system. In addition, many of the auditing and logging features can be turned on and off according to system needs. In this paper, we suggest a new solution for automatic upgrading without rebooting for applications developed using C++ programming language. Many implementations have been done to support dynamic code integration in C++. Most of these implementations addressed the issue of supporting dynamic classes or functions. None of these implementations addressed this at a coarse level like upgrading the application to include new security patch that may affect hundreds of application classes all at once. Also, consistency issue across different upgrades has been addressed at the class level rather than whole application level. Addressing consistency issue is extremely important in systems upgrading, especially when it comes to database applications. Aspect-Oriented Programming (AOP) [1] is an emerging technology for modularizing crosscutting concerns. We are applying AOP technology to the design and implementation of dynamically upgradeable systems. In general, complicated applications are characterized by having to get many pieces of software to work together seamlessly. AOP provides the ability to insert new behaviors into software systems, and to make these changes in a coordinated manner across a software system. In this paper, we describe DAO C++ and its applicability for safe application upgrades on the fly without restarting. Features of DAO C++ include: • Dynamic Loading. Aspects can be dynamically loaded into an executing system. Aspect code can change at run time. • Dynamic Weaving. Aspects can be woven and unwoven at run time using C++. • Efficient execution. Programs execute efficiently under normal operation. The rest of the paper is split into five sections. Section 2 introduces application upgrade scenario and the difficulties of implementing application upgrades. In Section 3, we describe our DAO C++ architecture after introducing Aspect-Oriented Software Development (AOSD). The implementation details of our system are explained in Section 4. A comparison to related work is presented in Section 5. We summarize the paper with Section 6.

2 Application Dynamic Upgrade Problem 2.1 Security Patch Upgrade Example Assume that we have an application, which contains code that implements the security policy of the application. Figure 1 shows an example about how a program may look like. Typically, application is represented as a set of classes. In general, the code that represents security is not centralized in one location. It crosscuts many classes as we see in Figure 1 where security code is marked by gray rectangles. If the design is extremely modular, it is possible to create one or more security code classes and invoke their methods from inside other application classes. This would be more modular but unfortunately, it will not be enough to achieve our goal of dynamically upgradeable system. Next section explains why it is not enough.

2

2.2 Need to Upgrade Assume that a new security protocol has been released for the application. This new security protocol requires modifying the existing security code and application. Possible modifications include: • Modifying a current security class or classes and replace it with a new version. • Modifying a method of a security class and replace it with a new version. • Adding new method(s) to a security class and inserting an invocation to the new methods from locations in other application classes. This affects the entire application because security code can be inserted at any place. • Inserting security code to places did not have it before. This can be represented by adding a new method calls inside the current application classes. For example in Figure 1, a new upgrade may insert security code inside class E. • Removing security code form places had it before. Removing method calls from inside the current application classes can represent this. For example in Figure 1, a new upgrade may remove security code form class B.

Figure 1. Security Protocol Crosscutting Example

2.3 How to Upgrade Dynamically To upgrade the application in a straightforward way, application developer team can modify the existing code to incorporate the new security code and then recompile it. Based on our example Figure 1, this step requires the developers to invasively change three program classes: B, C and D. If the security protocol crosscuts hundreds of classes, then system upgrade would be correspondingly more difficult and error-prone. Secondly, the “recompile” methodology requires stopping, reloading, and restarting the entire system. Such a restart destroys current sessions. If the security update requires changing multiple elements of the network (for example, to integrate biometric identity devices into the system) then the changes will need to be coordinated over a larger space. Of course, the problems of upgrading are not limited to security. They generalized to all protocols, services and devices. Current C++ programming implementations offer us capabilities to load classes dynamically. They work at the class level. This kind of dynamic code loading is not enough to implement all types of modification dynamically. For example, if the update includes adding new method(s) to a security class and insert an invocation to it from different places in other application classes, then this will require the application developer team to revisit the previously

3

programmed application and insert the new security code into current existing classes at the appropriate locations. This would be considered an invasive change because this may include visiting many application classes and modify them with the new security code. It will require a lot of work from the application developer team. In addition, it will be extremely difficult if not impossible to track all of the classes that got modified and generate library files for them and load them dynamically on the fly. Number of affected classes with the new update could be hundreds of classes or even more in complicated system. Even if we could do that, application consistency will be difficult to achieve specially in big applications. There are two factors that are making this upgrade difficult. First, the number of affected classes in the upgrade could be huge. Second, application consistency is difficult to achieve during switching from one code version to another. For example, assume we have a database transaction code that uses set of classes that got affected by the upgrade. Application can get into inconsistency status if part of the classes gets modified while transaction is running and other classes get modified after transaction is over.

3 DAO C++ for Upgrade without Restart Separation of Concerns (SoC) is a core principle in software engineering. Conventional software technology provides some mechanisms for separating concerns (principally subprograms and inheritance). However, we are coming to understand that every system decomposition has some concerns whose implementation crosscuts that decomposition. The driving idea behind the development of Aspect Oriented technology is to expand our ability to capture each concern as a modular software unit and to realize these concerns in actual systems. Keeping the representation of concerns separated throughout the software life cycle produces more flexible and reusable software. Aspect Oriented Software Development (AOSD), refers to the refinement of software according to the aspect-oriented process.

3.1 AOP terminology and concepts Our discussion is aided by introducing several AOP concepts and terminology Crosscutting concerns. Software development addresses concerns, both at the user/requirements levels and at the design/implementation level. Often, the implementation of one concern must be scattered throughout an implementation. We say that such a concern is crosscutting. For example, in many systems, the implementation of both high-level policies such as security, reliability, fault tolerance, accounting and manageability and lowlevel mechanisms such as caching and prefetching must be scattered in the implementation of many modules. Code tangling. In a conventional environment, implementing crosscutting concerns usually results in code tangling—the code for the several concerns becomes intermixed. Ideally, software engineering principles instruct us to modularize our system software in such a way that (1) each module is cohesive in terms of the concerns it implements and (2) interfaces between modules are simple. Software that complies with these principles tends to be easier to produce, more naturally distributed among different programmers, easier to verify and test, as well as easier to maintain, reuse, and evolve to future requirements. Crosscutting works against such modularization. Join points. Join points are places in the structure or execution flow of a program where additional behavior can be attached. A join point model provides the common frame of reference to enable the definition of possible join points. The most common join points are method calls, though aspect languages also have defined join points to a variety of other circumstances, including field definition, access and modification, exceptions, execution events and states.

4

Pointcut designator. A set of join points described by a pointcut designator. This is an important feature of AOP because it provides a quantification mechanism—a way to talk about doing something at many places in a program with a single statement. A programmer may designate all the join points in a program where, for example, a security code should be invoked. This eliminates the need to refer to each join point explicitly and reduces the likelihood that any aspect code would be incorrectly invoked. Advice. Advice is the behavior to execute at a join point. For example, this might be the security code to do authentication and access control. Many aspect languages provide mechanisms to run advice before, after, instead of or around join points of interest. Advice is oblivious in that there is no explicit notation at the joint point that the advice is to be run here—the programmer of the original base code may be oblivious of the evolving requirements. Aspect. An aspect is a modular unit designed to implement a concern. An aspect definition may contain some advice and the instructions on where, when and how to invoke them. Depending on the aspect language, aspects can be constructed hierarchically, and the language may provide separate mechanisms for defining an aspect and specifying its interaction with an underlying system. Weaving. Weaving is the process of composing core functionality modules with aspects. Aspect languages have defined several mechanisms for weaving, including static compilation, inserting aspects at system load, or dynamically recognizing when aspects should be run. Using AOP concepts, the security concern example used in section 2 is considered a crosscutting concern as shown Figure 2. In current technology implementations, its code will be tangled. The best way to represent it is as an Aspect so that the security code is completely localized in one place (Figure 2.a). We can use one or more aspects to represent the security concern. The security code (Tangled code) that is taken away from normal application classes is placed inside the security aspect and is called aspect advice. This code is replaced back into its different locations in application classes using the weaving process (Figure 2.b). The locations of code weaving are determined by two aspect properties: join-points and pointcut designator. The join points for our example represent all possible locations for a security code to be woven. Pointcut designator is used to decide which specific join points to use for the weaving process.

Figure 2. (a) Aspect Before Weaving

5

(b) Aspect After Weaving

Doing it this way, the normal application classes are clean from any security code. This makes it easier to modify security code if want to implement a new security policy or fix an existing one.

3.2 Why DAO C++? Although there are some implementations of AOP using C++, but none of these implementations satisfy our needs for dynamic safe upgrades without restart. None of them support dynamic weaving and unweaving at run-time. This feature is considered an essential element used to achieve our goal. They support only static weaving. In addition, most of these implementations require you to learn new language constructs. Because of these reasons, we have designed and implemented our own aspect-oriented language by extending C++ to include AOP support. The result was Dynamic Aspect Oriented C++ language. A comparison to other people related work is presented in section 5.

3.3 DAO C++ Basic Architecture DAO C++ is realized by using two components: a preprocessor and the AOP engine (Figure 3). The preprocessor works before compilation and generates the meta-object data needed at runtime. Moreover, the preprocessor modifies the original source code and inserts the minimal hooks for dynamic weaving. A minimal hook represents a piece of code that allows our system to weave in an aspect at the hook location if needed. Thus minimal hooks are inserted at all possible join points. In addition to adding hooks, the preprocessor modification includes adding generated meta-object data which becomes part of the new source code. After preprocessing, the new source code is passed to C++ compiler.

Figure 3: Preprocessing Stage of DAO C++

After compilation and loading, the running program contains meta-object data about program classes and methods. This information is used to achieve dynamic aspect weaving and unweaving by the AOP engine. The AOP engine works at run-time and accomplishes the following tasks: • Aspect Loading, Activation and Deactivation. • Run time Join-Point generation, Aspect Weaving and Aspect Unweaving. • Aspect Advice Execution. 3.3.1 Aspect Loading, Activation and Deactivation The AOP engine is responsible for loading aspects either at program loading time or any time later during program execution. During program load, aspect class code is loaded and activated. Aspect Loading includes finding the aspect class that could be in the local machine or at a remote machine. Aspect Activation includes registering the aspect code and making it the current running version. Aspect Activation takes place right after the loading step in the event we are loading the aspect for first time. But this is not the case if we are upgrading a current running aspect with a new loaded one. In the later case, we have to unweave the current aspect (if any) and deactivate the current aspect version. Other approaches could be followed but we decided to go with

6

deactivating the old aspect version and activating the new one, not allowing the two versions to coexist. Section 5 explains the different versions issue in detail. 3.3.2 Run time Join-Point generation, Aspect Weaving and Aspect Unweaving At run-time, an application user sends a request to the AOP engine to weave a specific activated aspect. As illustrated in Figure 4, the AOP engine weaves aspects by 1. Reading the Aspect pointcut designator, a user-defined expression. 2. Searching through meta-object data to find the matching classes/methods that match the aspect pointcut designator. 3. For all classes/methods that match the given aspect pointcut designator, activating and linking the appropriate minimal hooks to the given aspect. 4. Registering the aspect as woven.

Figure 4: AOP Engine at Run Time

On command to unweave an aspect, the AOP engine deactivates the corresponding hooks. Delaying aspect join-points generation to run-time adds extra overhead during weaving. However, this design decision supports greater flexibility, as it allows aspect scope to change at run time. This nice feature of aspect reshaping at run-time allows the user to minimize the number of aspects needed in addition to the number of weaving and unweaving steps. AOP engine supports refresh methods that can be called explicitly by application users to reflect latest aspect point cut designator. Other approaches can be followed for join-point generation like doing it at compilation time. This will lead to two system drawbacks: a less flexible aspect and extra space requirements. Nevertheless, it can improve system performance during weaving and unweaving time. 3.3.3 Aspect Advice Execution After an aspect has been woven, the program sequence of control may reach a specific minimal hook. The minimal hook code checks to see if this join point is enabled for a specific aspect(s). If enabled, control is passed from the program to the AOP engine, which runs the appropriate advice(s) for that join-point.

7

4 Implementation 4.1 Complete System Framework Figure 5 shows the complete system framework and how it works using our DAO C++ system. In the application provider side, each time a new or modified code is introduced, ( like a new security patch), application aspects will be compiled and corresponding library files will be generated. On the application client side, an aspect updater agent keeps checking for last application upgrades. In case of any new one, application user or administrator will be informed about it. Then based on user commanded request, aspect updater downloads the latest aspect(s) code and save it on the local aspect libraries directory. Then aspect updater informs the AOP engine of the new aspect arrival and request to upgrade it.

Figure 5: Complete System Architecture

4.2 Aspect and AOP_Engine Classes Implementation Before we get into details, let us take example that shows us the syntax of defining aspects and weaving/unweaving/refreshing them using our DAO C++ system. To include the capability into a normal C++ program, two classes should be used: the “Aspect” class, and the “AOP_Engine” class. The aspect class is used by application programmer to define their user-defined aspects. The programmer extends the aspect class by inheriting from Aspect class and overriding the definition of advice method. Moreover, the programmer defines aspect join-points by defining an aspect

8

pointcut designator. This can be done at aspect definition time or after aspect instantiation. Figure 6 illustrates the idea of aspect definition. The aspect pointcut designator has two main parts: class and method. The class part is used to narrow the scope of aspect join-points whenever we want to limit the weaving to a specific class. Class part can be ignored by using the wildcard character “%”. The method part defines the method signature that includes return type, method’s name, and method’s parameters. The special wildcard character can be used to replace any of the method signature’s parts. Additionally, a complex expression can be formed using the “or” operation represented by “||”. The “or” symbol will be part of the string and will be analyzed by the AOP engine during join-point creating time. Analogously, the “and” operation “&&” can be used. Besides the aspect pointcut designator, two boolean attributes are used to locate the type of advice execution: before method execution, after method execution or both. This structure of aspect defined as class, allows end user to define their own attributes and methods inside the aspect where these are accessed only locally from within the advice code. Defining a new method that accesses classes outside the aspect class scope can lead to indirect recursion and deadlock. class NewAspect : public Aspect { void advice( ) { advice code } } // end of aspect

In the main program: NewAspect na; na.setExpression( “class class_name_match, method method _signature() “) na.setBefor(true); na.setAfter(flase);

Figure 6: Aspect Definition in DAO C++

The second class that should be used by the end user is the “AOP_Engine” class. This class is already defined in a special C++ header file that has all of the related DAO C++ classes and Meta-object data related data structures. In order for an application user to weave a specific aspect, the AOP engine should load the aspect first and activate it (deactivate an old version if updating). Then, the application user/programmer invokes the weave method on a specific aspect. Similarly, user can invoke unweave and refresh methods.

4.3 Preprocessor Implementation Details The preprocessor works before the compilation stage and does the following: • Inserts minimal hooks in appropriate locations • Generates necessary Metaobject data for the run-time. The preprocessor performs the following actions: • It creates a class record for each parsed class. This class record holds information such as the class name, number of methods, and pointers to method records. • It creates a record for each method definition that holds information such as the method name, number of parameters, types of parameter and return type. • It inserts a minimal hook at the beginning of each method body, as shown in Figure 7. • It inserts a minimal hook at each possible method exit point. After preprocessing, the Metaobject data is be ready for instantiation during program loading time. Metaobject data includes two important objects JoinPoints and ClassInfo. JoinPoints is a list

9

of all possible join-points (hooks). Each inserted hook is linked to one of these join-points. The Classinfo object includes all necessary information for join-points generation at run-time. Figure 7 illustrates the idea of using Classinfo and JoinPoints.

4.4 Run-Time AOP Engine Implementation Details During program execution, all metaobject data is available to our AOP engine. When a user requests a specific aspect weaving, the AOP engine reads the aspect pointcut designator and scans its Classinfo object for matches. For each match, the corresponding join-point record is modified to reflect join-point activation, including registering woven aspects. Because minimal hooks are linked to join-points, this reflects the aspect weaving into the program. When the program sequence of execution reaches a specific minimal hook, control is passed from program to the AOP engine. Then, the AOP engine runs the appropriate advice(s) for whatever aspect(s) activated on that join-point. AOP engine utilizes the virtual function feature to call the advice function for each woven aspect.

Figure 7: Classinfo and JoinPoints objects generated by DAO C++preprocessor

For dynamic class loading, an array of aspects records information is used to keep track of active aspect information. Information includes the aspect’s name, a pointer to the aspect, its version number, an aspect semaphore and the source path. The aspect name is used to distinguish among the different aspects during aspect upgrading. The pointer variable is used to keep an instance of dynamically loaded aspect. The version number is used to keep track of aspect upgrades. The aspect semaphore is used by activate, deactivate and run advice methods to have mutual exclusion access to an aspect. The source path is used to keep track of path location information.

5 Related Work The idea of supporting dynamic Aspect weaving and unweaving is not new in AOP. Many techniques have been proposed and implemented. PROSE is one of the main ones [10]. PROSE is implemented on JVM and relies on built-in java reflection capabilities. The one language setup approach used by PROSE has been followed by our DAO C++ work. Just in Time (JIT) aspects allows dynamic weaving and unweaving more efficiently [11]. JIT is implemented in Java too. Handi-Wrap is implemented in Maya as an extension to the Maya programming language. Maya is a compile-time metaprogramming system that allows users to define language extensions [2]. JAC allows Java methods to be dynamically wrapped and unwrapped through a

10

classloader that performs bytecode rewriting [9]. JAC allows aspect to be unwrapped while Handi-Wrap allows only wrapping. It is interesting to know that JIT, Handi-Wrap and JAC use minimal hook weaving approach to support dynamic weaving. All of the techniques mentioned above are implemented using Java. Java performance makes it unsuitable for specific type of applications. AspectC++ supports AOP in C++[12]. Weaving is done at the compilation time like in AspectJ. This is known as static weaving. In static weaving, the concept of aspect does not exist at running time. So AspectC++ can not support aspect weaving at run time, a desired feature for some applications. Static weaving adds no overhead at the run-time. As in AspectJ [7], AspectC++ provides a rich set of joint point definitions and advice types that are not offered by our model for now. In the MOP area, many metaobject protocols have been designed. OpenC++ [4] and CLOS [8] are two common ones. In general, MOPs is used to reveal program information for debugging and performance analysis concerns. It allows programmers to view program metaobject data at compilation or run-time, but in general it does not allow program modification. Also, space complexity for these protocols is high. As a consequence, we decided to design our own simple MOP. Techniques for dynamically changing a running C++ program already exist. Dynamic C++ Classes allow run-time updates of an executing C++ program at the class level [6]. Also, many dynamic linkers have been introduced for C and C++ [6]. All of these techniques work at the class or function level. They do not recognize the concept of aspect.

6. Summary and Future Work The paper presented DAO C++ language as the first language to support dynamic aspect weaving and unweaving at run-time for C++. DAO C++ has been designed and implemented with networking applications in mind. In particular, it provides dynamic adaptability of network services and keeps the performance acceptable. DAO C++ realizes this trade off by implementing a language structure to provide dynamic weaving and unweaving of aspects at run-time. It also supports dynamic aspect loading at run-time. Our experiment with the system shows that DAO C++ provides the needed capabilities for dynamic applications upgrading on the fly. Implementing system features as a form of aspects whenever apply simplifies the overall dynamic upgrade process. DAO C++ is in its early versions. Current DAO C++ supports a subset of AOSD capabilities and our goal is to provide full AOSD capabilities in coming versions. Also, the challenge of supporting consistent dynamic weaving process in multithread environment with high performance needs extra research. Finally, the performance of DAO C++ during weaving process needs improvement. Using a virtual machine that supports dynamic point cut can improve performance.

7. References [1] M. Akşit, S. Clarke, T. Elrad, and R. Filman. Aspect Oriented Software Development. To be published by Addison-Wesley, Reading Mass., 2004. [2] J. Baker and W. Hsieh. Runtime Aspect Weaving Through Metaprogramming. In 1st Intl. Conf. on Aspect-Oriented Software Development, Enschede, The Netherlands, pages 86-95, Apr. 2002. [3] M. Barr, S. Eisenbach. Safe Upgrading without Restarting. In International Conference On Software Maintenance, Page 129-137. September 2003. [4] S. Chiba. A Metaobject Protocol for C++. In Proc. ACM Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), page 285-299, October 1995. [5] T. Elrad, R. Filman, and A. Bader. Aspect-oriented programming. Comm. ACM, 44(10) 2001, 29–32.

11

[6] G. Hjálmtýsson, and R. Gray. Dynamic C++ Classes A lightweight mechanism to update code in a running program , in Proc. USENIX Annual Technical Conference, pp. 65-76, June, 1998 [7] G. Kiczales, E. Hilsdale, J. Hugunin, M. Kersten, J. Palm, and W. Griswold, An overview of AspectJ. Proc. ECOOP 2001, LNCS 2072 (Berlin, June 2001), J. L. Knudsen, Ed., Springer-Verlag, 327–353. [8] G. Kizales, J. des Rivieres, and D.G. Bobrow, The Art of the Metaobject Protocol. The MIT press, 1991. [9] R. Pawlak, L. Seinturier, L. Duchien, and G. Florin. JAC: A Flexible solution for aspect-oriented programming in Java. In Reflection 2001, pages 1–24, Koyota, Japan 2001. [10] A. Popovici, T. Gross, and G. Alonso. Dynamic Weaving for Aspect Oriented Programming. In 1st Intl. Conf. On Aspect-Oriented Software Development, Enschede, The Netherlands, Apr. 2002. [11] A. Popovici, G. Alonso, and T. Gross. Just-in-Time Aspects: Efficient Dynamic Weaving for Java. In Proc. 2nd International Conference on Aspect-Oriented Software Development. Boston, Pages: 100 – 109, 2003

[12] O. Spinczyk, A. Gal, and W. Schröder-Preikschat. AspectC++: An aspect-oriented extension to the C++ programming language. Fortieth International Conference on Technology of Object-Oriented Languages and Systems (TOOLS Pacific 2002), volume 10 of Conferences in Research and Practice in Information Technology. ACS, 2002.

12