A Mechanism for Runtime Evolution of Objects Yasuhiro Sugiyama Department of Computer Science Nihon University Koriyama, Japan sugiyama@ce.nihon-u.ac.jp 1. Runtime Version Management of Objects for Software Evolution Recent computer programs written in object-oriented programming languages, like C++ and Java, are composed of many objects. I am currently working on a mechanism that allows objects in programs to evolve during the execution. I allow objects to get new functionality during the execution. Bugs in objects may be fixed while the program is running. New objects may be added to running programs. My approach is based on the version management of objects. I allow objects to have multiple versions. Versions of objects to be used in a program can be selected and changed while the program is running. Objects may be replaced with new versions that have new functionality. Objects with bugs may be replaced by new versions of the objects that fix the bugs during the execution. New objects may be added to existing programs during the execution. In [2], I introduced the indirect dynamic linking mechanism that allows versions of library functions to be replaced at runtime. I extended the indirect dynamic linking mechanism to objects so that versions of objects can be replaced during the execution. This paper will give a brief overview of my approach for runtime version management of objects for software evolution. This paper is organized as follows. In the next section, I will briefly describe the notion of versions of objects. Three types of versions: variations, revisions, and mirrors will be introduced. In the third section, I will describe the mechanism for the runtime evolution of these three types of versions. I implemented my version management mechanism for Java objects. Final section will conclude this paper with some discussions to be considered in the future. 2. Versions of Objects It is a common practice [1] to categorize versions of computer programs as variations and revisions. For instance, Linux, a popular operating system in these days, has many versions. RedHat Linux, TurboLinux, and LinuxPPC are variations of Linux. On the other hand, RedHat Linux 2.1 and RedHat Linux 2.2 are revisions of RedHat Linux. 1
I conceive three types of versions of objects: revisions, variations, and mirrors. In all the three cases, I will assume that versions are immutable, that is, modification of an existing version implies the creation of a new version. 2.1. Revisions of Objects A revision of an object is an object that is created to replace the original object. Versions to be created for bug fixes and/or functional enhancements are typical examples of revisions. Revisions are not supposed to be used simultaneously with the original object. Revisions of an object form a sequence of versions in which each version in the sequence is supposed to supercede its predecessor versions. Typically, the latest revision is most significant and appropriate for use. But, sometimes, the latest revision may introduce a new bug. In these cases, older revisions may be used instead. Revisions may be created preserving and inheriting the internal state of their predecessor versions. It is quite common that a new revision is introduced during the execution of the original object due to bugs found in the original object. It is necessary to replace the buggy original version with the new revision that fixes the bug without loosing the internal state of the original object. External interface of revisions may evolve. New methods may be introduced in a new revision. Methods in a revision may be revised in a successor revision. Please note, although external interface of revisions may evolve, objects that call the revised objects also need to be revised at the same time so that they can invoke the newly introduced methods. 2.2. Variations of Objects A variation of an object is an object that is created as an alternative of the original object. Two or more variations of a single object may exist simultaneously. All the variations and the original object can be used interchangeably. Two or more implementations of a single class for different operating systems are typical examples of variations. An object may have two or more immediate revisions. In this case, all the revisions that are immediate successors of a single object are variations. All the variations of an object share a common external interface, while they may have different internal implementation. Some of them may have different functionality, while others may have different implementations of the same functionality. Unlike revisions, variations do not depend on the original object and/or other variations each other. Variations are not required to preserve the internal state of other variations. All the variations of a single object are independent entities. 2.3. Mirrors of Objects Objects may be accidentally destroyed during the execution due to hardware and/or software problems. In these cases, if copies of the objects exist, the current state of the 2
crashed objects may be easily recovered and the objects might be able to resume the execution. Particularly, if copies exist on two or more machines, the entire program may keep running even if one of the machines crashes. Mirrors are versions to be created for a backup purpose. A mirror of an object is simply a copy of the original object. Objects may have any number of mirrors. All mirrors of a single object keep the same internal state, and are updated simultaneously. 3. Runtime Version Management of Java Objects 3.1. Revision Management When a new revision is introduced during the execution of the original object due to bugs found in the original object, it is necessary to replace the buggy original version with the new revision that fixes the bug without stopping the execution of the entire program. I introduce the revision management mechanism using proxy objects [3]. Each object has its own proxy object. An object and its revisions share a single proxy object. User programs, that use objects with revisions, are bound to these proxies, but not to the objects themselves. The proxies take care of forwarding and/or redirecting requests from user programs to the objects. Upon receipt of requests from the user programs, the proxies select an appropriate revision, and forward the requests to the selected revision. Revisions can be selected at the loading time as well as at the execution time. A new revision may be created preserving the internal state of its predecessor version. I use derivation [4] that allows incremental refinement of objects without loosing the internal state of objects. Derivation allows us to avoid migration and some other expensive conversion tasks from older revisions to newer revisions. In order to define a new revision of an object, one must define a class of the revision, which is a derived class of the class of the original object. Please note that, although the C++ community often uses the words derived classes and sub-classes interchangeably, derived classes and sub-classes are different concepts here. I extended the syntax of Java so that derivation can be defined in Java programs, although I do not have space here to discuss the new notation. I developed a preprocessor to translate Java programs with derivation into regular Java notation. I also developed the proxy generator for Java that reads the source code of a regular Java class and generates a class of the proxy objects for the instances of the class. This revision management mechanism works for Java applications as well as Java Applets. When one tries to download an Applet with multiple revisions into a Web browser, the Web browser will download the byte code of its proxy class, instead of the byte code of the Applet itself, from the Web server. The Web browser will instantiate the proxy object, and thereafter the proxy object will take care of downloading the byte code of the necessary revisions from the Web server during the execution. 3
3.2. Variation Management Like revisions, in order to define a new variation, one must define a class of the variation. I use simple naming convention to distinguish classes of variations. I enhanced the class loader of the Java Virtual Machine (JVM) so that one of the variations can be selected and loaded at the loading time of the class. One may consider that variations are Java classes that implement a single Java interface. However, I do not put this restriction at this moment. Any classes with the same external interface can be variations. Variations to be used in a program may be changed during the execution. I use proxy objects to take care of dynamic replacement of variations, like replacement of revisions. This mechanism allows one to use variations as hot-swappable plug-ins for Java programs. However, when a variation is switched to another variation, the context of the old variation may be completely lost, and the context of the new variation will be used thereafter. My mechanism leaves the implementation details of each variation completely for the developer of the variation. For instance, a variation may be designed so that, even after the variation is switched to another variation, one may return to the original variation and resume its execution. Another variation may be designed to terminate the execution when it switches to another variation. I also enhanced the Web server Apache so that Java Applets can have multiple variations. When a Web browser downloads a class, it sends a request to an appropriate Web server. The Web server will select an appropriate variation, and will send the byte code of the selected variation to the browser. 3.3. Mirror Management I use proxy objects to maintain mirrors. When a user program instantiates a class with mirroring capability, a proxy object is created first, and is bound to the user program. The proxy object generates an appropriate number of instances of the class, and will maintain them as mirrors. When the proxy receives a request to update the object, the proxy forwards the request to all the mirrors, so that all the mirrors can keep the same state. Proxy objects are capable of laying out mirrors depending on the configuration of machines that run the program. All the mirrors may be created on a single machine in some cases, while mirrors may be distributed to machines that are connected by a network in some other cases. In the latter case, I use Java RMI to implement method invocation of mirrors on remote machines. 4
4. Conclusions I am now in the last phase of implementing the runtime version management mechanism of Java object described in this paper. I do not have sufficient data to evaluate my approach yet. However, I conducted several tests, and my tests showed that the use of proxy objects results in the reduction of the execution speed of many Java programs, although I do not have space here to show my numerical data. I am investigating two reasons for the speed reduction. First reason is the use of indirect dynamic linking in proxy objects. Method invocations through proxy objects always take extra steps, but the speed reduction rate is low. I would like to conclude this is a quite reasonable cost that we need to pay to achieve the dynamic nature of the runtime version management. Second reason is the use of Java reflection in proxy objects. Use of Java reflection is quite essential to allow dynamic evolution of external interfaces of revisions. Proxy objects use Java reflection to find methods available in new versions that might not exist when the proxy objects are created. However, in my tests, Java reflection showed significant overhead, compared to the indirect dynamic linking. I am now working on this problem. One possible solution to this problem is simultaneous replacement of revisions of related objects. As I mention in the previous section, even if a new method is introduced in a new revision of an object, its caller objects cannot use the new methods, unless they are implemented so that they can search new methods in the revision. Otherwise, the caller objects need to be revised at the same time with the called object. If simultaneous replacement of revisions of caller and called objects is possible, we do not need to use the proxy object for the called object any more, although proxy objects for caller objects are still necessary for their version management. This will contribute to improve the runtime efficiency of my approach. References [1] W. A. Babich, Software Configuration Management, Addison-Wesley, 1986. [2] Y. Sugiyama, Runtime Software Evolution based on Version Management, IWPSE99, April, 1998. [3] Y. Sugiyama, A Mechanism for Runtime Object Evolution based on Version Management, JSSST'98, September, 1998, in Japanese. [4] Y. Sugiyama, Producing and Managing Software Objects in the Process Programming Environment OPM, APSEC'94, December, 1994. 5