Defining and Implementing Variables
When defining your own device/agent/server context variables, it's necessary to specify properties of their definitions, i.e. name, description, format, readable/writable flags, permission level, help text, and group. To declare new variable, create an instance of VariableDefinition
object and set its properties. Here is an example:
// Creating String field format
FieldFormat ff = FieldFormat.create("demoSettingField", FieldFormat.STRING_FIELD);
// Creating single-cell (scalar) setting
TableFormat format = new TableFormat(1, 1, ff);
// Creating variable (setting) definition. Note that variable group should not be changed.
VariableDefinition vd = new VariableDefinition("demoSetting", format, true, true, "Demo Setting", ContextUtils.GROUP_REMOTE);
// Setting permission level
vd.setPermissions(ServerPermissionChecker.getManagerPermissions());
Once done, add the variable definition to a context:
Device driver context's variables matching device access settings should be added by calling
Context.addVariableDefinition()
from inside theDeviceDriver.setupDeviceContext()
method.Definitions of device driver context's variables matching device properties (device settings variables) should be returned by overridden
DeviceDriver.readVariableDefinitions()
method.Server plugins should add variables from inside
install()
andstart()
methods.Java-based Agents should add variables after
Agent
object creation viaAgent.getContext().addVariableDefinition()
.Finally, scripts (both server and widget scripts) should not normally add any variables.
For server-side variables, it might be also necessary to specify read/write permission level:
// Setting permission level
vd.setPermissions(ServerPermissionChecker.getManagerPermissions());
![]() | Device setting variables provided by a device driver, along with Agent variables, must belong to a group |
Variable group is specified by calling VariableDefinition.setGroup()
. You can use the following syntax:
vd.setGroup(ContextUtils.createGroup(ContextUtils.GROUP_REMOTE, "Power Management Settings", "Auto Power-off"));
Manually added variables (except for device setting variables) should also have a non-null getter implementing VariableGetter
interface. The purpose of getter is providing custom variable reading code. Writable variables should also have a non-null setter implementing VariableSetter
interface. The setter should somehow store new variable value modified by system operators or different server facilities.
![]() | Format of data table returned by variable getter must match format contained in its definition. Failure to follow this rule may result in data loss since the system will try convert the data table to definition-provided format preserving as much data as it can. |
Variable Value Getting Implementation
This section comprehensively describes how a Context
builds and returns a DataTable
representing variable value once someone has called the getVariable()
method.
All logic described here is implemented in the AbstractContext
class that is included into the open-source SDK.
To return a resulting DataTable
from the getVariable()
method, an AbstractContext
does the following:
Obtains variable read lock to prevent concurrent read operations. If any other thread reads the variable, current thread will block until the previous read operation is finished.
Checks whether the calling party (identified by the
CallerController
object) has sufficient effective permission level in the current context to read this variable.
Searches current context (using Java reflection technology) for existence of the "getter method" with the following signature:
public DataTable getV
variable(VariableDefinition def, CallerController caller, RequestController request) throws ContextException
The variable is the name of variable being read.
If a method with such a signature is found it will be called. This method must return a DataTable
representing value of the variable.
If a "getter method" was not found, existence of
VariableGetter
is checked by callingVariableDefinition.getGetter()
. IfVariableGetter
is defined (not null),VariableGetter.get()
method is called. The getter must return aDataTable
representing value of the variable.
If variable getter is not defined (i.e.
VariableDefinition.getGetter()
returns null), theAbstractContext
callsgetVariableImpl()
method. This method may be overridden in the subclasses ofAbstractContext
. If the overriding implementation "understands" the variable (i.e. "knows" its name), it should return aDataTable
representing value of the variable. In other cases, thegetVariableImpl()
method should return null.
If
getVariableImpl()
method has returned null, theAbstractContext
callsexecuteDefaultGetter()
method. The default getter returns value of the variable that was stored in the database (for server-side code) or cached in memory (for all other cases).
If variable value was not previously cached,
executeDefaultGetter()
method checks whether default value of the variable is defined (non-null) by callingVariableDefinition.getDefaultValue()
. This value should be set viaVariableDefinition.setDefaultValue(DataTable value)
earlier.
And finally, if default value is not defined in the
VariableDefinition
, the default getter constructs and returns a default value by callingnew SimpleDataTable(variableDefinition.getFormat(), true)
.
DataTable
obtained from "getter method", VariableGetter, getVariableImpl() method, or default getter is not immediately returned fromgetVariable()
. It's first checked for accordance with variable format (available viaVariableDefinition.getFormat()
, this step is skipped if it returns null). If format of the data table matches format provided by the definition (or extends it by adding some fields), this table is returned as is. If format of table does not match or extend format of definition,AbstractContext
does its best to convert the table to "proper" format and preserve as much data as possible. This is done by callingDataTableReplication.copy()
method.
![]() | If code implementing "getter method", VariableGetter or getVariableImpl() needs to access database-stored or memory-stored value of the variable, this code should call |
Variable Value Setting Implementation
This section comprehensively describes how a Context
processes a DataTable
representing new variable value once someone has called the setVariable()
method.
All logic described here is implemented in the AbstractContext
class that is included into the open-source SDK.
To process a DataTable
representing new value of the variable when the setVariable()
method is called, an AbstractContext
does the following:
Obtains variable write lock to prevent concurrent write operations. If any other thread writes the variable, current thread will block until the previous write operation is finished.
Checks whether the calling party (identified by the
CallerController
object) has sufficient effective permission level in the current context to write this variable.
DataTable
representing new value of the variable is first checked for accordance with variable format (available viaVariableDefinition.getFormat()
, this step is skipped if it returns null). If format of the value data table matches format provided by the definition (or extends it by adding some fields), this table is left intact. If format of value table does not match or extend format of definition,AbstractContext
does its best to convert the table to "proper" format and preserve as much data as possible. This is done by callingDataTableReplication.copy()
method.
If the calling party has permission checking enabled (i.e. represents a normal system user), the system ensures that values of all read-only fields in the new table are equal to values of corresponding fields in the old (current) table that is internally retrieved by
getVariable()
call. Values of all changed read-only fields are reset to the ones taken from old (current) table.
Current context is then searched (using Java reflection technology) for existence of the "setter method" with the following signature:
public void setV
variable(VariableDefinition def, CallerController caller, RequestController request, DataTable value) throws ContextException
The variable is the name of variable being written.
If a method with such a signature is found it will be called. This method must process the DataTable
representing new value of the variable.
If a "setter method" was not found, existence of
VariableSetter
is checked byVariableDefinition.getSetter()
. IfVariableSetter
is defined (not null),VariableSetter.set()
method is called. The setter must process theDataTable
representing new value of the variable.
If variable setter is not defined (i.e.
VariableDefinition.getSetter()
returns null), or the setter has returned false, theAbstractContext
callssetVariableImpl()
method. This method may be overridden in the subclasses ofAbstractContext
. If the overriding implementation "understands" the variable (i.e. "knows" its name), it should process aDataTable
representing new value of the variable and return true. Otherwise, thesetVariableImpl()
method should return false.
If
setVariableImpl()
method has returned false, theAbstractContext
callsexecuteDefaultSetter()
method. The default setter stores new value of the variable in the database (for server-side code) or memory cache (for all other cases).
![]() | If code implementing "setter method", VariableSetter or setVariableImpl() needs to store new variable value in the database or memory, this code should call |
Was this page helpful?