Архитектурные особенности LUWRAIN

Подпроект core — центральный компонент платформы LUWRAIN. Он содержит основной исполняемый код, который собирается в главный JAR-файл luwrain.jar, и предоставляет базовую функциональность для разработки приложений и расширений. Ниже рассмотрена архитектура этого подпроекта с особым вниманием к встроенной поддержке сценариев.

Основные абстракции. Архитектура ядра LUWRAIN основана на нескольких ключевых абстракциях, которые образуют каркас всей платформы.

Интерфейс org.luwrain.core.Luwrain служит основной точкой доступа приложений и расширений к службам ядра. Через него можно управлять синтезом речи, выводом на брайлевский дисплей, воспроизведением звуков, рабочими областями, запуском приложений, буфером обмена, конфигурацией и другими возможностями платформы. Каждое приложение и расширение получает собственный экземпляр Luwrain. Благодаря этому ядро может определять источник запросов и разграничивать уровни доступа к системным функциям.

Приложения. Любое интерактивное приложение LUWRAIN реализует интерфейс org.luwrain.core.Application. К его основным методам относятся:

  • onLaunchApp(Luwrain) — выполняет первоначальную инициализацию приложения;
  • getAreaLayout() — возвращает компоновку рабочих областей;
  • onAppClose() — вызывается при завершении работы приложения.

Интерфейс MonoApp расширяет Application и предназначен для приложений, которые могут быть запущены только в одном экземпляре.

Рабочие области. Фундаментальной единицей пользовательского интерфейса LUWRAIN является область — area. Область представляет собой интерактивный рабочий объект, например поле текстового ввода, структурированный документ, дерево или форму. Она формирует текстовое представление содержимого, обрабатывает события ввода и управляет «горячей точкой» (hot point). Горячая точка определяет текущую позицию взаимодействия пользователя с содержимым области и фактически выполняет роль фокуса.

Расширения. Интерфейс org.luwrain.core.Extensions определяет основной контракт для подключения внешних расширений. Расширения загружаются с помощью механизма ServiceLoader. Они могут предоставлять приложения и команды для их запуска, фабрики компонентов панели управления, средства интернационализации и другие объекты, дополняющие возможности платформы.

События и речевой вывод. События ввода, представленные классом org.luwrain.core.events.InputEvent, и системные события, представленные классом org.luwrain.core.events.SystemEvent, передаются активным областям. В ответ области формируют объекты EventResponse, которые, помимо прочего, управляют речевой обратной связью. Непосредственное взаимодействие с подсистемой синтеза речи осуществляется через класс org.luwrain.core.Speech и интерфейс org.luwrain.speech.Engine.

Одной из важных архитектурных особенностей LUWRAIN является встроенная поддержка сценариев, реализованная в пакете org.luwrain.script и его подпакетах. Сценарии позволяют расширять функциональность платформы без предварительной компиляции Java-кода. Для их выполнения используется JavaScript-среда GraalVM. Загрузкой сценариев управляет ExtensionsManager. При запуске ядро сканирует каталоги js/, расположенные как в общей структуре платформы, так и внутри установленных пакетов, и ищет файлы с расширением .js.

Имена файлов должны соответствовать шаблону <компонент>-<имя>.js. Примерами допустимых имён являются core-startup.js и core-commander.js.

Класс org.luwrain.script.core.ScriptCore управляет жизненным циклом сценариев. Он реализует интерфейс org.luwrain.core.HookContainer, благодаря чему сценарии могут регистрировать обработчики системных событий в виде хуков. При загрузке сценария ScriptCore создаёт новый экземпляр класса Module. Этот объект инкапсулирует контекст GraalVM и отвечает за выполнение сценария.

Система хуков. Сценарии регистрируют обработчики событий с помощью метода Luwrain.addHook(name, func). Ядро запускает зарегистрированные обработчики посредством метода HookContainer.runHooks(). Пакет org.luwrain.script.hooks предоставляет несколько стратегий выполнения хуков:

  • ChainOfResponsibilityHook реализует цепочку обязанностей: хуки вызываются последовательно, а первый обработчик, вернувший true, останавливает дальнейшее выполнение цепочки;
  • ProviderHook реализует стратегию поставщика и возвращает первый результат, отличный от null;
  • CollectorHook вызывает все хуки и собирает полученные результаты;
  • PermissionHook используется для проверки разрешений, при которой каждый хук должен вернуть true;
  • NotificationHook вызывает все зарегистрированные хуки, игнорируя возвращаемые ими значения;
  • TransformerHook последовательно передаёт значение через цепочку хуков, позволяя каждому из них преобразовать результат предыдущего обработчика.

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