<- The JWIG Runtime System Contents Updating a Running Service ->

Serialization of Shared Data

All fields in the service class are automatically shared between all running threads in the service. This provides a simple alternative to full-scale databases for non-critical data. Shared data fields are read and written just as any other variable, and synchronization and other concurrency control issues can be handled by the standard Java mechanisms, such as synchronized methods and statement blocks.

Example:

    public class Game extends Service {
        protected int visitors = 0;

        synchronized int hit() {
            return ++visitors;
        }
        ...
    }

In this example, a shared field visitors is declared. Only one instance of this field exists for the entire service. The method hit, which writes to the field, is synchronized to avoid race conditions with other concurrently executing threads.

For critical data, external databases can be used, for instance via JDBC, as in any other Java program. However, much data in typical Web services does not require the high performance and stability of a large database, and the extra complexity of the service code for building database requests can be significant.

Transactions and Serialization

To increase robustness of the services, JWIG contains a primitive transaction system, which allows the shared state to be stored to disk at well-defined places during the execution. The default mechanism is based on Java's built-in serialization mechanism.

Using the checkpoint and rollback methods, the programmer can decide when to take a snapshot of the shared state and store it on disk and also to restore the state as it was at the last snapshot:

class dk.brics.jwig.runwig.Service
public static void checkpoint()
                       throws java.io.IOException
Serializes all shared service data and stores it in jvm.state.
Throws:
java.io.IOException - if an I/O error occurred

class dk.brics.jwig.runwig.Service
public static void rollback()
                     throws java.io.IOException,
                            java.lang.ClassNotFoundException
Restores all shared service data from jvm.state.
Throws:
java.io.IOException - if an I/O error occurred
java.lang.ClassNotFoundException - if the class of a serialized object cannot be found

This approach requires all shared state to be declared as Serializable. All data that is transitively reachable from the service object is stored, except for transient and static fields.

The checkpoint method should be invoked only at times when the shared state is consistent. The runtime system ensures that the checkpoint is performed atomically. If the JVM is suspended, as described in the previous section, a checkpoint is automatically performed, and when it resumes, a rollback is performed. Also, if the server or the JVM should crash, a rollback is automatically performed when the server is up again and a new thread is started by a client.

Serialization using XML

As an alternative to the standard serialization mechanism described above, JWIG also allows the shared state to be stored using an XML representation. Using such a representation has the benefit that other tools straightforwardly can read and modify the data, if the need should arise. A service class which implements the XMLSerializable interface is serialized and unserialized using the methods toXML and fromXML, which the programmer must implement. The XML documents are represented using JDOM in the program code:

class dk.brics.jwig.runwig.XMLSerializable
public org.jdom.Element toXML()
Creates XML representation of this object. References to objects must be followed manually.
Returns:
JDOM XML tree

class dk.brics.jwig.runwig.XMLSerializable
public void fromXML(org.jdom.Element e)
Restores the state of this object according to the given XML representation.
Parameters:
e - JDOM XML tree

Example:

    public class MyService extends Service implements XMLSerializable {
        class Person {
            String login;
            String password;
            String name;
            
            Person(String login, String password, String name) {
                this.login = login;
                this.password = password;
                this.name = name;
            }
        }
    
        HashMap people = new HashMap();
    
        public Element toXML() {
            Element e = new Element("people");
            Iterator i = people.values().iterator();
            while (i.hasNext()) {
                Person p = (Person) i.next();
                Element f = new Element("person").
                    addContent(new Element("login").addContent(p.login)).
                    addContent(new Element("password").addContent(p.password)).
                    addContent(new Element("name").addContent(p.name));
                e.addContent(f);
            }
            return e;
        }
        
        public void fromXML(Element e) {
            people = new HashMap();
            Iterator i = e.getChildren().iterator();
            while (i.hasNext()) {
                Element f = (Element) i.next();
                Person p = new Person(f.getChildText("login"),
                                      f.getChildText("password"),
                                      f.getChildText("name"));
                people.put(p.login, p);
            }
        }
    
        synchronized void addPerson(String login, String password, String name) {
            people.put(login, new Person(login, password, name));
            log(Log.INFO, "added user: "+login);
            try { 
                checkpoint();
            } catch (IOException e) {
                log(Log.ERR, "unable to checkpoint");
            }
        }
        ...
    }

In this example, the people field contains a set of Person objects, which are shared between all service threads. The toXML methods constructs an XML tree containing all the information from the people set, and fromXML replaces the current shared state by the data in the given XML tree. Also note that the addPerson method is synchronized to avoid concurrency problems.

At runtime, the serialized state is stored in a file named jvm.state.CLASS located in the service directory, where CLASS is an ASCII encoding of the service class name.


<- The JWIG Runtime System Contents Updating a Running Service ->