Cloud Softphone and all apps based on Acrobits SDK allows various configurations to be provisioned from the server. At the same time, the apps often contain “settings” or “preferences” GUI which lets the end user set various configuration options.
In such a situation, collisions are possible. In case the values from settings GUI and from provisioing are simply saved, values entered by end user will be overwritten by provisioning and vice versa, which leads to chaos and bad user experience.
To overcome this, we created a simple system of merging the values based on priorities, which may be either set explicitly, or be deduced automatically from where the values came from.
As the name suggests, Mergeable XML works with XML documents. These documents are treated as key-value stores, where names of nodes which are children of the root nodes are the keys and content of these nodes are values. The node values may be strings - text data inside the nodes, or xml sub-trees, in which case, the whole sub-tree is taken as a single value.
Mergeable XML document can be seen as a list of key-value pairs, where keys are strings and values may be either strings, or XML sub-trees.
<account> <host priority="10" source="provisioning">example.com</host> <transport priority="20" source="extProv">udp</transport> <rewriting priority="40" source="gui"> <rule> <conditions> <condition type="doesntStartWith" param="+"/> </conditions> <actions> <action type="prepend" param="+420"/> </actions> </rule> </rewriting> </account>
This example defines a key-value store of three entries: host=>example.com, transport=>udp and rewriting=>(xml tree). This is an actual example of Account XML, which uses Mergeable XML model.
The XML nodes which are immediate children of root node will be called
key nodes and the content of these nodes
(either strings or XML sub-trees) will be called
Every key node in Mergeable XML must have two attributes:
source is a human readable name which explains where does the key node came from (for example
gui etc.) and its value is not used for anything else than troubleshooting.
This attribute contains an integer value in decimal notation. This value specifies the priority of this particular key-value entry, which determines whether this value is going to be overwritten or not.
Existing key node is overwritten by an incoming key node ONLY if the priority of the incoming key node is same or higher than priority of the existing key node. In case the key-node IS overwritten, the incoming key node value and attributes replace the existing key node value and attributes.
In case the app stores some data as Mergeable XML, the values may never be written directly. The only way to modify this data is by merging-in other Mergeable XML. The process of merging decides whether the values should be written or not based on key node priorities.
The incoming XML to be merged in may come from various sources: provisioning from Cloud Softphone portal, response of
some provisioing web service or it may be created by the app GUI when the user saves the form. The attributes
source don’t need to be always explicitly defined; in case they are not set, the app will deduce
them based on the origin of the data and assign a default priority:
|default||0||default value, hardcoded in the app|
|provisioning||10||values coming from Cloud Softphone portal|
|extProv||20||values coming from external provisioning web services|
|gui||40||values set by end users using GUI|
|system||100||values set by system which should never be overwritten|
Priorities of 100 and higher are reserved and should not be used. The app stores various persistent data using these priorities and accidentally overwriting them may cause undefined behavior.
In case of string values, it is possible to specify them either as text contents of the XML node, like shown in the
example above for
host, or as a xml tree with multiple
<value>> nodes for different languages.
<dialAction id="recordedCall"> <displayName priority="20" source="extProv"> <value lang="cs">Nahrávaný hovor</value> <value lang="jp">記録されたコール</value> <value>Recorded Call</value> </displayName> </dialAction>
The key node
dialAction is a localizable node. The app will pick the string from
<value> subnode which matches
the current device locale. If such node is not found, the app will take the value from
<value> subnode which does
not contain a
lang attribude - this node serves as a default. If such a node also doesn’t exist, the app uses the
text contents of key node, just like in non-localized case.
Let’s assume the app has an account with values and priorities as in the previous example. The user opens
“edit account” form and decides to change the
tcp. When saving the form, the app internally
generates the following Mergeable XML:
<account> <transport priority="40" source="gui">tcp</username> </account>
It will then try to merge this document with the one stored in the app. Since the incoming priority of 40 is higher than the stored priority of 20, the merge will be performed and the resulting document will look as follows:
<account> <host priority="10" source="provisioning">example.com</host> <transport priority="40" source="gui">tcp</transport> <rewriting priority="40" source="gui"> <rule> <conditions> <condition type="doesntStartWith" param="+"/> </conditions> <actions> <action type="prepend" param="+420"/> </actions> </rule> </rewriting> </account>
If later, the external provisioning web service is called again and it sends the key node
transport in its response,
the app will assign default priority of 20 to this node and attempt to merge it. This time, the value will not be
written, because the existing priority of 40 is higher and the user-entered value stays.
In case the provider later decides to explicitly set the
transport key node priority higher, it can return the
following node in the external provisioning response:
<account> <transport priority="50" source="extProv">url</transport> </account>
This time, the value will be merged, because the priority is higher than the stored 40. When the user opens “edit
account” form now, he will see the
transport field as read-only, because the GUI is intelligent and knows that
any new value would get a default priority of 40 and would not beat the existing priority of 50.
In case the value is XML tree, the value is taken as a whole. If the external provisionig web service sends a node
<rewriting priority="50"> in its response, the whole content of the incoming node will be stored. The merging
process is not recursive.
Some values coming from provider’s web services may not be merged even if the priority is set high enough. In case the incoming value would enable functionality which is not included among the selected Cloud Softphone features on Cloud Softphone portal, the relevant key node will be filtered out even before merging is attempted.