Определение и реализация переменных

При определении переменных контекста ваших устройств/агентов/серверов необходимо задать свойства их определений, т.е. имя, описание, формат, флажки чтения/записи, уровень прав доступа, текст справки и группу. Для объявления новой переменной создайте экземпляр объекта VariableDefinition и задайте его свойства. Вот пример:

// 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());

После этого добавьте определение переменной к контексту:

  • Переменные контекста драйвера устройства, соответсвующие настройкам доступа устройства, должны добавляться вызовом Context.addVariableDefinition() из метода DeviceDriver.setupDeviceContext().

  • Определения переменных контекста драйвера устройства, соответствующие свойствам устройства (переменные настроек устройства), должны возвращаться переопределенным методом DeviceDriver.readVariableDefinitions().

  • Плагины сервера должны добавлять переменные из методов install() и start().

  • Агенты на базе Java должны добавлять переменные после создания объекта Agent через Agent.getContext().addVariableDefinition().

  • Наконец, скрипты (как скрипты сервера, так и виджета) обычно не должны добавлять никаких переменных.

Для переменных сервера также необходимо задать уровень прав доступа для чтения/записи:

// Setting permission level

vd.setPermissions(ServerPermissionChecker.getManagerPermissions());

Переменные настроек устройства, предоставляемые драйвером устройства, вместе с переменными Agent должны принадлежать группе remote. Если вы хотите сгруппировать их во вкладки, добавьте описание вкладки к имени группы, используя разделитель "|", например remote|Power Management Settings. Также возможно использовать вложенные группы, например remote|Power Management Settings|Auto Power-off.

Группа переменных определяется вызовом VariableDefinition.setGroup(). Можно использовать следующий синтаксис:

Переменные, добавляемые вручную (кроме переменных настроек устройства), должны также иметь ненулевой метод чтения, реализующий интерфейс VariableGetter. Цель метода чтения - обеспечить пользовательский код чтения переменной. Записываемые переменные должны также иметь ненулевой метод установщика, реализующий интерфейс VariableSetter. Метод установщика должен каким-то образом сохранять новое значение переменной, измененнное системными операторами или различными интсрументами сервера.

Формат таблицы данных, возвращенный методом установщика переменной, должен соответствовать формату, содержащемуся в ее определении. Несоблюдение данного правила приведет к потере данных, поскольку система попытается сконвертировать таблицу данных в формат, предоставленный определением, сохраняя как можно большее количество данных.

Реализация чтения значения переменной

Этот раздел подробно описывает, как Context выстраивает и возвращает DataTable, представляющую значение переменной, когда кто-то вызывает метод getVariable().

Вся логика, описанная здесь, реализуется в классе AbstractContext, включенном в комплект разработчика с открытым исходным кодом.

Чтобы вернуть результирующую DataTable из метода getVariable(), AbstractContext выполняет следующее:

  • Применяет блокировку чтения переменной для предотвращения одновременных операций чтения. Если любой другой поток читает переменную, текущий поток заблокируется до завершения предыдущей операции чтения.

  • Проверяет, имеет ли вызывающая сторона (определенная объектом CallerController) необходимый эффективный уровень прав доступа в текущем контексте для чтения этой переменной.

  • Ищет текущий контекст (используя технологию отражения Java) и проверяет его на наличие "метода чтения" со следующей сигнатурой:

public DataTable getVvariable(VariableDefinition def, CallerController caller, RequestController request) throws ContextException

Variable - это имя читаемой переменной.

Если находится метод с такой сигнатурой, он вызывается. Этот метод должен вернуть DataTable, представляющую значение переменной.

  • Если "метод чтения" не был найден, наличие VariableGetter проверяется вызовом VariableDefinition.getGetter(). Если VariableGetter определен (не ноль), вызывается метод VariableGetter.get(). Метод чтения должен вернуть DataTable, представляющую значение переменной.

  • Если метод чтения переменной не определен (т.е. VariableDefinition.getGetter() возвращает ноль), AbstractContext вызывает метод getVariableImpl(). Этот метод может быть переопределен в подклассах AbstractContext. Если реализация переопределения "понимает" переменную (т.е. "знает" ее имя), она должна вернуть DataTable, представляющую значение переменной. В других случаях метод getVariableImpl() должен вернуть ноль.

  • Если метод getVariableImpl() вернул ноль, AbstractContext вызывает метод executeDefaultGetter(). Метод чтения по умолчанию возвращает значение переменной, которая хранилась в базе данных (для кода сервера) или была кэширована в память (для всех других случаев).

  • Если значение перменной до этого не кэшировалось, метод executeDefaultGetter() проверяет, определяется ли значение переменной по умолчанию (ненулевое) вызовом VariableDefinition.getDefaultValue(). Это значение должно быть ранее задано через VariableDefinition.setDefaultValue(DataTable value).

  • И, наконец, если значение по умолчанию не определяется в VariableDefinition, метод чтения по умолчанию выстраивает и возвращает значение по умолчанию вызовом new SimpleDataTable(variableDefinition.getFormat(), true).

  • DataTable, полученная из "метода чтения", методов VariableGetter, getVariableImpl() или метода чтения по умолчанию не сразу возвращается из getVariable(). Сначала она проверяется на соответствие формату переменной (доступно через VariableDefinition.getFormat(), этот шаг опускается, если возвращается ноль). Если формат таблицы данных соответствует формату, предоставленному определением (или расширяет его, добавляя некоторые поля), эта таблица возвращается в исходном виде. Если формат таблицы не соответствует и не расширяет формат определения, AbstractContext конвертирует таблицу в "нужный" формат и сохраняет максимальное количество данных. Это делается вызовом метода DataTableReplication.copy().

Если коду, реализующему "метод чтения", VariableGetter или getVariableImpl(), нужен доступ к значению переменной, хранящейся в базе данных или памяти, этот код должен напрямую вызывать метод executeDefaultGetter().

Реализация установки значения переменной

Этот раздел подробно описывает, как Context выстраивает и возвращает DataTable, представляющую значение переменной, когда кто-то вызывает метод setVariable().

Вся логика, описанная здесь, реализуется в классе AbstractContext, включенном в комплект разработчика с открытым исходным кодом.

Чтобы обработать DataTable, представляющую новое значение переменной при вызове метода setVariable(), AbstractContext выполняет следующее:

  • Применяет блокировку записи переменной для предотвращения одновременных операций записи. Если любой другой поток записывает переменную, текущий поток заблокируется до завершения предыдущей операции записи.

  • Проверяет, имеет ли вызывающая сторона (определенная объектом CallerController) необходимый эффективный уровень прав доступа в текущем контексте для записи этой переменной.

  • DataTable, представляющая новое значение переменной, сначала проверяется на соответствие формату переменной (доступному через VariableDefinition.getFormat(), этот шаг опускается, если возвращается ноль). Если формат таблицы данных значения соответствует формату, предоставленному определением (или расширяет его путем добавления полей), эта таблица остается нетронутой. Если формат таблицы значений не соответствует и не расширяет формат определения, AbstractContext коновертирует таблицу в "нужный" формат и сохраняет максимальное количество данных. Это выполняется вызовом метода DataTableReplication.copy().

  • Если у вызывающей стороны включена проверка уровня прав доступа (т.е. как у обычного системного пользователь), система проверяет, чтобы значения всех полей "только для чтения" в новой таблице были равны значениям соответствующих полей в старой (текущей) таблице, которая внутренне извлекается вызовом getVariable(). Значения всех измененных полей "только для чтения" переустанавливаются на те, которые взяты из старой (текущей) таблицы.

  • Ищет текущий контекст (используя технологию отражения Java) и проверяет его на наличие "метода установщика" со следующей сигнатурой:

public void setVvariable(VariableDefinition def, CallerController caller, RequestController request, DataTable value) throws ContextException

Variable - это имя записываемой переменной.

Если находится метод с такой сигнатурой, он вызывается. Этот метод должен вернуть DataTable, представляющую новое значение переменной.

  • Если "метод установщика" не находится, наличие VariableSetter проверяется при помощи VariableDefinition.getSetter(). Если определяется VariableSetter (не ноль), вызывается метод VariableSetter.set(). Метод установщика должен обработать DataTable,представляющую новое значение переменной.

  • Если метод установщика переменной не определен (т.е. VariableDefinition.getSetter() возвращает ноль) или метод установщика возвращает false, AbstractContext вызывает метод setVariableImpl(). Этот метод может быть переопределен в подклассах AbstractContext. Если реализация переопределения "понимает" переменную (т.е. "знает" ее имя), она должна обрабатывать DataTable, представляющую новое значение переменной и возвращать true. В ином случае метод setVariableImpl() должен вернуть false.

  • Если метод setVariableImpl() вернул false, AbstractContext вызывает метод executeDefaultSetter(). Метод установщика по умолчанию сохраняет новое значение переменной в базе данных (для кода сервера) или в кэше памяти (для всех других случаев).

Если коду, реализующему "метод установщика", VariableSetter или setVariableImpl(), нужно хранить значение новой переменной в базе данных или памяти, этот код должен напрямую вызывать метод executeDefaultSetter().