The Source for Java Technology Collaboration


Home | Changes | Index | Search | Go

Shared cell state

This proposal is for a simple shared state mechanism in Wonderland. The idea is to provide a simple Map that can be attached to a Wonderland cell. Values put into the map by one client will be accessible to all other clients in view of the given cell. In addition, the Map will allow the user to register listeners to be notified when values change.

This type of mechanism is useful for a number of common tasks, including manipulating state from a script, building shared models for user interface components, and building simple cells without requiring server programming.

Basic usage

Shared state is implemented as a standard Wonderland cell component. The functionality -- encapsulated in the SharedStateComponent on the client and SharedStateComponentMO on the server -- can be added to any Wonderland cell. The SharedStateComponent provides an interface for managing SharedMaps. The SharedStateComponent manages any number of shared maps, differentiated by name. Cell authors may use this name binding in whatever manner they choose, but the multiple names are designed to allow different cell components to segment their state into separate maps to avoid key collisions.

The SharedMap is an extension of the standard Java Map interface. In general, it supports all the methods of Map with a few minor additions. A small number of standard Map functions, such as clear() may not be implemented on a SharedMap. The SharedMap maps from String keys to instances of the SharedData interface, described below. When a key is added to, modified in, or removed from a SharedMap, the change is propagated to all clients with instances of the given component (typically, all clients in view of the cell containing the SharedStateComponent). All changes pass through the Darkstar server, ensuring a consistent ordering of all messages received by every client. Thus the SharedMap provides a distributed Map functionality across multiple clients. SharedMap also contains listeners, to provide notification whenever a value is changed in the map. These listeners differ on the client and the server, so are described separately in the client and server documentation below.

SharedData is a tagging interface added to various data types that are designed to be stored in the SharedMap. By design, the SharedMap cannot be extended by the user to support arbitrary data types. This limitation ensures that data can be serialized effectively both in a binary form in the Darkstar server and in a XML form for server coldstart. The table below lists the data types proposed for the shared map:

Data Type Description
SharedInteger A holder for the primitive int type
SharedString A holder for the standard String class
SharedBoolean A holder for the standard boolean type
SharedFloat A holder for the standard float data type
SharedVector3f A holder for a 3D point data type
SharedTransformMatrix A holder for a 3D transform matrix data type
SharedUserID A holder for a user identifier

As mentioned above, shared maps are designed to be persistent -- all values set in the Map will persist in the Darkstar server for the short term and in XML for the long term. This means that any clients interacting with the map will be able to retrieve values from the map immediately on receiving the SharedMap object.

Client API

The SharedMapCli interface extends the SharedMap to include client-side listeners. Listeners -- defined by the SharedMapListenerCli interface -- will be notified of remote property changes to the Map. Much like the standard Observable interface, clients will be notified of the property, its previous value and its current value in the the listener's callback method. The callback also contains an identifier for the session that originated the change, so clients can easily differentiate between changes from different clients. The SharedMapCli interface allows listeners to be registered either for all property changes, or only for properties matching the given regular expression.

Local vs. remote changes

When a client makes a change to the SharedMap, this constitutes a local change. Local changes to the map are persisted in that client's Map, so that a call to put() followed by a call to get() will returned the recently added value. Later, the Darkstar server will process the change and send the resulting event to all clients, including the originating client. When the originating client receives a message from the server, its local map is now in sync with all other clients.

Additionally, the Darkstar server may choose to deny a particular change. In this case, the value in the local value in the client's map will revert to the previous value sent by the server. The client that originated the change will receive an extra event, notifying all event listeners of the change from the local value back to the previously sent remote value.

Server API

The SharedMapSrv interface extends the SharedMap interface to add server-side listener capabilities. The server side listener will be notified of all remote changes to the Map. In addition to notification, the server-side listeners can also veto changes made by the client. When a change is vetoed, the master copy of the Map is not changed, and the client that sent the original message is notified that its change has been rejected.

Note that on the server, there is no differentiation between local and remote changes. All changes made on the server are sent to all clients as soon as the relevant Darkstar transaction commits. Server-side listeners, however, are only notified of remote changes, not of ones initiated locally.

Example code

The SharedMap is designed to work like a normal Map:

// find the shared state component from the cell
SharedStateComponent ssc = cell.getCellComponent(SharedStateComponent.class);

// get a named map
SharedMapCli map = ssc.get("testmap");

// get a value from the map
map.get("a");

// put a value into the map
map.put("b", SharedInteger.valueOf(2));

Listeners may be added to process when values are submitted to the Map.

map.addSharedMapListener(new SharedMapListenerCli() {
    public void propertyChanged(SharedMapCli map, BigInteger senderID,
                                String key, SharedData prevVal,
                                SharedData newVal)
    {
        String prevStr = (prevVal == null) ? null : prevVal.toString();
        String newStr = (newVal == null) ? null : newVal.toString();

        System.out.println("Map " + map.getName() + " Update from: " + senderID);
        System.out.println("  " + key + " prev: " + prevStr + " new: " + newStr);
    }
});

Implementation notes

The implementation of SharedMap currently supports all Map operations except clear().

The implementation is mostly lazy: when a client requests a Map, the server sends all the keys in the Map, but none of the values. Values are filled in the first time the client requests the value, or when a message is received because another client has changed the value of the key. The SharedStateComponent also "snoops", so any changes sent to it are recorded, even for Maps the client has not yet requested. These values are stored until the client requests the given Map.

Topic WonderlandSharedCellState . { Edit | Ref-By | Printable | Diffs r2 < r1 | More }
 XML java.net RSS

Revision r2 - 25 Jan 2009 - 23:24:53 - Main.kaplanj
Parents: WebHome > ProjectWonderland > WonderlandRoadmap > WonderlandReleasepoint5