Run batch changes to application state

It is possible to make changes to several elements in the application state tree in a single operation. For instance, you could batch write a new value to all paths that meet a certain condition, or batch update all the children of a given parent node.

Batch operations require that you first explicitly lock application state if you are developing in a multi-threaded language.

 

For historical reasons, the names of the classes discussed in this section vary from API to API. Rather than repeat these differences throughout the text, they are listed below:

  • C++: TypelessStateManager, TypelessStateLock classes, and the Typeless namespace
  • .Net, Java, HTML5 and Android: XmlStateManager, XmlStateLock, and XmlUtility
  • iOS: PWXmlStateManager, PWXmlStateLock, PWXmlUtility, and PWXmlElement

How-to

Work with state locks

APIs:

AcquireLock()
C++ | .Net | Java | iOS | Android

The PureWeb APIs automatically lock your local application state during read and write operations. However, in some situations it is necessary to explicitly set the lock:

  • If you want the lock to be held longer than for just a single read or write operation. For instance, if you define a value changed handler that checks the validity of the value before writing it to application state, you should lock application state at the start of the handler.
  • If you provide a call to another function as an argument of setValue, since it will be necessary for that function to run and return its value before that value can be set in application state.

  • If you want to perform atomic changes to multiple leafs of the application state tree, for example when using setTree (discussed further down this page).

  The only exception is the HTML5 client. Since JavaScript is single-threaded, it doesn't require explicit locks. Instead, you have the option to suppress events during batch operations if needed, as discussed further down this page.

Locks are acquired using the AcquireLock method. (In service APIs, you can also use StateManager.LockAppState(), it works the same way and both methods can be used interchangeably.)

Once the lock is acquired, you use the methods in the "StateLock" class to interact with application state, such as setValue and getValue.

Locks will be automatically released when the StateLock object ceases to exist (when the variable no longer has scope), but you can also explicitly release a lock using the releaseLock method (called release in the Java/Android APIs).

The syntax will look something like this:

On the service

C++

MyClass::FunctionThatNeedsLock()
{
    // Acquire the lock
    TypelessStateLock lock = m_stateManager.AcquireLock();

    // Perform operations while the lock is held, for example:
    lock.SetValue('/path', 'value');
}

.Net

FunctionThatNeedsLock()
{
    using (XmlStateLock lock = StateManager.Instance.LockAppState())
    { lock.SetValue('/path', 'value'); }
}

Java

FunctionThatNeedsLock()
{
    // Set the lock (assuming stateManager is your private reference to that variable)
    XmlStateLock lock = stateManager.acquireLock();

    // Perform operations while the lock is held
    lock.setValue('/path', 'value');
}

On the client

HTML5

// JavaScript is single-threaded and doesn't use locks. 
// See next section, about suppressing events.

iOS (Obj-C)

FunctionThatNeedsLock()
{
    // Set the lock
    PWXmlStateLock *stateLock = [_stateManager acquireLock];

    // Perform operations while the lock is held, for example:
    [stateLock setValue: @"/path" value:@"value"];
}

Android

FunctionThatNeedsLock()
{
    // Set the lock
    XmlStateLock lock = XmlStateManager.acquireLock();

    // Perform operations while the lock is held, for example:
    lock.setValue('/path', 'value');
}


Suppress state changed events

APIs:

startChanges()
C++ | .Net | Java | HTML5 | iOS | Android

finishChanges()
C++ | .Net | Java | HTML5 | iOS | Android

The XmlStateManager raises an event whenever a change occurs in application state; this event is mostly used internally by the APIs. However, in a batch operation, you may need to delay the firing of this event until the very end, to prevent the corresponding handlers from running too soon.

To do this, you would call startChanges at the beginning of the batch operation to temporarily suppress the events, and finishChanges at the end to release the suppressed events.

In HTML5 clients, this approach is the alternative to using state locks.


Write trees of elements

APIs:

setTree()
.Net | Java | HTML5 | iOS | Android

In all service and client APIs except C++, you can use setTree to write a sub-section of the application state as a tree of elements. This does not change the specified path, only its children.

  The setTree method is not available in In C++. You must use setValue instead, and repeat as needed.

This method takes an XML element which can be inserted into the path specified in the arguments.

The objects that store XML trees are platform-specific, represented in the native XML data type for that language: XElement (.Net), Element (Java and Android), XmlElement (HTML5), or PWXmlElement (iOS).

 

The only API to deviate from this model is HTML5, where you can express the tree either as a JSON object or as XML element. Using JSON objects is more common, as it is a more intuitive format for storing complex data in JavaScript. Under the hood, the JSON will be converted to XML.

The APIs provide an XmlUtility class to manipulate XML elements. (In iOS, there is also a PWXmlElement class for this.)

On the service

C++

// The SetTree method is not available in C++.

.Net

var parentNode = new XElement("MyParentNode");
XElement firstChild = new XElement("firstChildKey", "firstChildValue");
XElement secondChild= new XElement("secondChildKey", "secondChildValue");

parentNode.Add(firstChild);
parentNode.Add(secondChild);

myStateManager.XmlStateManager.SetTree("/", parentNode);

Java

Element parentNode = new Element("MyParentNode");
Element firstChild = new Element("FirstChildKey");
firstChild.setText("FirstChildValue");
Element secondChild = new Element("SecondChildKey");
secondChild.setText("SecondChildValue");

parentNode.addContent(firstChild);
parentNode.addContent(secondChild);	
stateManager.setTree("/", parentNode);

On the client

HTML5

var parentNode = pureweb.xml.XmlUtility.newXMLElement('MyParentNode');
var firstChild = pureweb.xml.XmlUtility.newXMLElement('firstChildKey');
pureweb.xml.XmlUtility.setText({ xml : firstChild, value : "firstChildValue" });
var secondChild = pureweb.xml.XmlUtility.newXMLElement('secondChildKey');
pureweb.xml.XmlUtility.setText({ xml : secondChild, value : "secondChildValue" });

parentNode.appendChild(firstChild);
parentNode.appendChild(secondChild);

pureweb.getFramework().getState().getStateManager().getState().setTree('/', parentNode);

iOS

PWXmlElement *parentNode = [PWXmlElement elementWithXMLString:@"MyParentNode" error:nil];
PWXmlElement *firstChild = [PWXmlElement elementWithXMLString:@"firstChildKey" error:nil];
[firstChild setStringValue:@"firstChildValue"]
PWXmlElement *secondChild = [PWXmlElement elementWithXMLString:@"secondChildKey" error:nil];
[secondChild setStringValue:@"secondChildValue"]

[parentNode addContent:firstChild];
[parentNode addContent:secondChild];

[state.stateManager setTree:@"/" node:parentNode];

Android

Element parentNode = new Element("MyParentNode");
Element firstChild = new Element("FirstChildKey");
firstChild.setText("FirstChildValue");
Element secondChild = new Element("SecondChildKey");
secondChild.setText("SecondChildValue");

parentNode.addContent(firstChild);
parentNode.addContent(secondChild);	

stateManager.setTree("/", parentNode);


Get all the children of a path

APIs:

getTree()
.Net | Java | HTML5 | iOS | Android

Just like you can use setTree to batch write all the children of a parent, you can use getTree to retrieve all a parent's children in a single operation.

This is very similar to getValue, except that instead of returning a single value, the method returns a sub-section of the application state tree.

The returned object will be an XML element.

  In the HTML5 API, getTree returns a JSON object. If you prefer to get the tree as an XML element, use getTreeAsXml instead.

On the service

C++

// The GetTree method is not available in C++.

.Net

m_stateManager.XmlStateManager.GetTree("/MyParentNode");

Java

m_XmlStateManager.getTree("/MyParentNode");

On the client

HTML5

pureweb.getFramework().getState().getStateManager().getTree('/MyParentNode');

iOS (Obj-C)

[[PWFramework sharedInstance].state getTree:@"/MyParentNode"];

Android

framework.getState().getTree("/MyParentNode");