|
A Simple ExampleThe following program, which is a trivial currency converter, provides a simple example of how to write a program using OPJ. The program allows the current exchange rate to be set, which it remembers, and then performs conversions using it. First, here is the program as it would be written in a non-persistent Java platform. One thing to note is that we create a singleton instance of the Main class and store the reference in a static variable called self. We could just as easily have avoided the singleton instance and declared exchangeRate static, but we wanted to force a more object-oriented style which we would expect in a larger application.Assuming that we have setup our environment to contain the Java tools, we can compile and run the application as follows: javac ccalc.java java ccalc Input command: c100 Amount 100.0 converts to 100.0 Input command: x1.65 Exchange rate set to 1.65 Input command: c100 Amount 100.0 converts to 165.0 Input command: q While the program is running it correctly remembers the latest value of the exchange rate. However, if the computer crashes or if we exit the program with a q command, then the next time it runs the exchange rate will be reset to its initial value. To make the exchange rate persistent we can use OPJ to checkpoint the state of the application each time around the while loop. Assuming that the checkpoint is successful, this will guarantee that the persistent store will contain the computation which will resume with the most recently set exchange rate value. Here is the modified program. This program contains just three lines that are related to OPJ. These are highlighted in red. The first addition is the import of the OPJ API. The second line is really only necessary because threads are not persistent. The statement OPRuntime.roots.add(Main.class) makes the Main.class object a root of persistence by adding the object to the root set. Finally, the call to OPRuntime.checkpoint() causes a checkpoint to happen each time around the while loop. The checkpoint causes all classes loaded by the bootstrap class loader to be made persistent. When a java.lang.Class object is made persistent, the static variables and the bytecodes for the method bodies are also made persistent, as are any objects reachable from the static variables, transitively. Since the class for Main was stored in the OPRuntime.roots variable, it will be made persistent. As a result, the static variable self will be made persistent, which will make the singleton instance of Main and hence the value of exchangeRate persistent. Note that we could just as easily made the singleton instance of Main be the persistent root and done away with the self variable altogether. The end result would be equivalent because if an instance is persistence reachable, so is its class. Note that other object persistence mechanisms sometimes use a map from names (Strings) to objects as the way to denote persistent roots. Indeed, earlier versions of OPJ used this mechanism. It is perfectly straightforward to create such a mechanism using the class-based root table and one way to achieve this is described here. Note also that some programs can be run under OPJ with absolutely no changes at all, by judicious use of special options to the opj virtual machine. See here for more details. Before we can run the example under OPJ, we must create a configuration file that describes the location and characteristics of the persistent store. For this simple example we can just edit a copy of the example small configuration file that is provided with the OPJ release, and change the <Log> and <Store> definitions. We will assume that the configuration file is saved in "/home/user/jane/ccalc.pjc". The following command can then be used to run the program: opj -config /home/user/jane/ccalc.pjc opjtutorial.ccalc.Main Input command: c100 Amount 100.0 converts to 100.0 Input command: x1.65 Exchange rate set to 1.65 Input command: c100 Amount 100.0 converts to 165.0 Input command: qSo far this is just the same as in the non-persistent case. If we now run the program again and convert 100 units, we see that the exchange rate value retains its value from the last checkpoint. opj -config /home/user/jane/ccalc.pjc opjtutorial.ccalc.Main Input command: c100 Amount 100.0 converts to 165.0 Input command: qIn the above example it was not difficult to work around the absence of persistent threads, essentially because the main method recreates the invariants that are assumed at the head of the while loop. The exception, of course, is the value of the exchangeRate variable itself. Were this value declared as a local variable of the main method, the application would not work correctly without persistent threads, because the value would be on a thread stack. The example works because exchangeRate is a field of the class and because we added the line to make the class object a persistent root. What would happen if we removed the call to OPRuntime.checkpoint? Doing so would change the semantics of the program slightly. Because of the implicit checkpoint that occurs whenever the OPJ Virtual Machine terminates normally, in this case when the "q" command calls System.exit(0), the value of exchangeRate will be made persistent on exit. However, if its value is changed, but the program crashes before exiting, this update would be lost. So, in general, frequent explicit checkpoint calls can reduce the risk of work being lost. This example uses a very simple data structure, just a single variable of type float, for its state. An important property of OPJ is that, irrespective of how complicated the data structure is, the application would work with the same simple changes. In particular, no changes would be needed to the supporting data structures themselves. We could, for example, substitute a third-party fixed-point decimal class, with no need even to recompile it. This property is called persistence independence and is one of the defining principles of orthogonal persistence.
PREV:
Introduction, NEXT:
Evolution,
Tutorial Home Page
Last changed: Nov 15th 1999 Questions and comments to forest-info@sunlabs.com | ||||||||||||||||||||||