State Management APIs

Registering State

A Message State must be registered with a Model for it to be used. This registry is done as part of the design of the Model.

In creating a custom Message Type that supports state:

Create a subclass of ORSetHelper and add it to the MessageModel. Instantiation is usually done in the parse(MessagePart messagePart) method as this is when the root MessagePart will be known:

@Override
protected void parse(@NonNull MessagePart messagePart) {
    // {Message parsing}

    orSetHelper = new MyOrSetHelper(getGson(), getRootMessagePart());
}

The helper class encapsulates the logic for making and retrieving selections. The type of the ORSet is defined in the createORSet method:

@Override
protected ORSet createORSet(String stateName, 
	    LinkedHashSet<OrOperation> adds, 
	    LinkedHashSet<String> removes) {
	switch (stateName) {
        case "favorite_colors":
            return new StandardORSet(stateName, adds, removes);
        case "latest_color":
            return new LastWriterWinsRegister(stateName, adds, removes);
    }
}

Types of State

The following types of state properties can be registered:

Type Description
StandardORSet Any value that is added via an add operation will be added to the Set of values if not already present; remove operations will similarly be allowed from the Set
FirstWriterWinsRegister Whatever Response Message reaches the server first gets its value accepted; all others are rejected
LastWriterWinsRegister Whatever Response Message reaches the server last gets its value accepted; all others are overwritten. One can not remove values via the remove operation
LastWriterWinsNullableRegister Same as LastWriterWinsRegister, but one can use remove operations to unset the value

Setting State

Message state can be set using methods provided by Message Models:

orSetHelper.processLocalSelection(getAuthenticatedUserId(), "favorite_colors", true, "red");
orSetHelper.processLocalSelection(getAuthenticatedUserId(), "latest_color", true, "red");

The above code snippet will add red to the favorite_colors Set, and will update latest_color to be red.

Note that state is written on a per-user basis. If User A sets latest_color to be red and User B sets it to be blue both states will be part of the message and indexed by the user’s Identity ID.

Sharing State

Once the state has been updated, it must also be shared with the server and other participants; this is done by generating and sending a Response Message.

A Response Message consists of:

  1. A Message Part instructing the server on the changes to be made to the Message whose state is to be changed
  2. A Message Part containing a Status Message to inform users of what change has just occurrred

The following APIs are used to generate the Response Message:

Change operations are added to the ORSetHelper using the processLocalSelection method (the selected argument determines if it should be added or removed):

List<OrOperationResult> results = new ArrayList<>(2);
results.addAll(orSetHelper.processLocalSelection(
	    getAuthenticatedUserId(), "favorite_colors", false, "blue"));
results.addAll(orSetHelper.processLocalSelection(
	    getAuthenticatedUserId(), "latest_color", true, "red"));

Each call will add operations to the result list. A ChoiceResponseMetadata object should then be created. This is where the text for the Status Message is added:

String statusText = "Frodo the Dodo has removed blue as a Favorite Color";
ChoiceResponseMetadata responseMetadata = new ChoiceResponseMetadata(
        getMessage().getId(), 
        UUID.fromString(getRootPartId().getLastPathSegment()), 
        statusText, 
        results);

The response message can now be sent by using the MessageSenderRepository:

getMessageSenderRepository().sendChoiceResponse(getMessage().getConversation(), responseMetadata);

Retrieving State

Because state is written on a per-user basis, state must be retrieved using the user’s Identity ID:

Set<String> favorites = orSetHelper
        .getSelections("layer:///identities/FrodoTheDodo", "favorite_colors");
Set<String> latest = orSetHelper
        .getSelections("layer:///identities/FrodoTheDodo", "latest_color");

Detecting State Changes

Each time a Response Message is sent, there will be state changes in the Message that you operated upon. All participants will see these state changes. The state changes involve adding or updating a Response Summary Message Part. Clients detect this Response Summary’s changes and provide APIs around it.

When state changes, a model’s processResponseSummaryMetadata method is called. When using an ORSetHelper, all you have to do is pass the metadata on to that class:

@Override
protected void processResponseSummaryMetadata(@NonNull ResponseSummaryMetadataV2 metadata) {
    orSetHelper.processRemoteResponseSummary(metadata);
}

This will handle updating the ORSetHelper and locally caching the data so further changes can be made.

Introduction Response Messages