Полетаев В. А. Разработка протокола обмена сообщениями для системы удаленного управления программным обеспечением сервера / В. А. Полетаев, А. В. Чернышова // Материалы V Международной научно-технической конференции Современные информационные технологии в образовании и научных исследованиях (СИТОНИ—2017) / Донец. национал. техн. ун-т; — Донецк,2018. — C. 42–46 [Ссылка на сборник]
УДК 004.043

Использование средств рефлексии языка Java при реализации протоколов прикладного уровня

Полетаев В. А., Кузьмичева А. С.
Донецкий национальный технический университет, г. Донецк
кафедра программной инженерии

Полетаев В. А., Кузьмичева А. С. Использование средств рефлексии языка Java при реализации протоколов прикладного уровня. Рассмотрены возможные методы реализации приложений, осуществляющих обмен информацией с удаленными устройствами. Обосновано использование Java Reflection API для организации отношений компонентов системы.

Ключевые слова: гибкая архитектура, средства рефлексии, сериализация, обработка данных

Общая постановка проблемы

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

Это требование можно удовлетворить, если на этапе проектирования разработать модульную архитектуру с низкой сопряженностью компонентов [1]. В случае если модуль реализует некоторый публичный интерфейс и инкапсулирует большую часть своей логики, то внесение изменений в модуль, не приведет к необходимости изменения других компонентов системы, или эти изменения окажутся минимальными.

Рассмотрим возможный вариант проектирования компонента системы, выполняющего обмен сообщениями с удаленным приложением (рис. 1).

Многоуровневая архитектура приложения
Рисунок 1 — Многоуровневая архитектура приложения

Функция обмена информацией с удаленным устройством во многих случаях может быть реализована средствами стандартной библиотеки многих современных языков программирования. Для этого могут быть использованы транспортные протоколы стека TCP/IP, беспроводная передача данных с использованием Bluetooth, передача данных по проводному соединению (USB и др.) или передача информации в пределах одной системы средствами межпроцессного взаимодействия. Большинство этих способов подразумевают передачу сообщений в виде последовательности октетов.

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

Проблемы отображения данных на динамические структуры данных

Одним из способов решения этой проблемы является десериализация сообщения в некоторую динамическую структуру данных, которая позволяет установить соответствие между кодом поля сообщения и его значением. Для этого необходимо разработать базу данных, содержащую коды сообщений и типы полей. Пример фрагмента базы данных в формате YAML, которая содержит информацию о сообщениях протокола, приведен на рисунке (рис. 2).

Фрагмент формата базы данных сообщений протокола
Рисунок 2 — Фрагмент формата базы данных сообщений протокола

Этот подход позволяет успешно абстрагироваться от средства взаимодействия, однако он обладает существенными недостатками:

Этих проблем можно избежать путем объединения информации о структурах данных и соответствующем им формате передачи. Для этого необходимо использовать средства рефлексии, доступные во многих современных языках программирования.

Сериализация данных

Средства рефлексии Java позволяют определить для любого класса, интерфейса или члена класса метаданные, которые могут быть получены и обработаны программой на этапе выполнения [2]. Эта возможность позволяет объединить информацию базы данных, описанной выше с определениями полей класса, на которые данные сообщения будут отображены. При отправке сообщения, подлежащего сериализации, средствами интроспекции программа может определить тип сериализуемых данных и получить информацию об аннотированных полях класса.

Для десериализации сообщения необходимо определить, какой класс соответствует конкретному типу сообщения. Для этого в формат каждого сообщения можно добавить код типа. Классу, соответствующему этому типу можно прикрепить аннотацию, параметром которой является код типа. Это позволяет установить взаимно-однозначное соответствие между сообщением и классом.

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

Реализация модуля сериализации

Рассмотрим пример реализации модуля сериализации, организованного с использованием рефлексии и интроспекции.

Программные модули, реализующие сериализацию и десериализацию типов данных, могут быть организованы в виде классов, связанных отношением обобщения с абстрактным классом ByteConverter. Эти классы должны переопределить абстрактные методы serialize и deserialize. Они получают и записывают последовательность октетов, используя потоки вывода и ввода соответственно. Для определения типа данных, которые подлежат преобразованию, класс-наследник должен сопровождаться одной или несколькими аннотациями @SerializableType, параметром которой является тип сериализуемого значения.

Диаграмма, иллюстрирующая фрагмент модуля сериализации, включающего поддержку целочисленного типа представлена на рисунке (рис. 3). Стандарт UML не поддерживает добавление метаданных к элементам диаграммы классов, однако существуют исследования, направленные на разработку возможных расширений стандарта, обеспечивающих возможность проектирования приложений с использованием аннотаций Java и атрибутов .NET Framework [3]. Один из этих вариантов используется в тексте данной статьи.

Диаграмма модуля сериализации, включающего поддержку целочисленного типа
Рисунок 3 — Диаграмма модуля сериализации, включающего поддержку целочисленного типа

Сериализация и десериализация скалярных типов, таких как целочисленные переменные, значения с плавающей запятой, строки происходит чтением или записью в поток данных определенного количества октетов, в зависимости от типа.

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

Необходимо отметить, что информация о параметрах типов в Java удаляется на этапе компиляции. Это связано с использованием механизма стирания типов (type erasure) [4], обеспечивающего обратную совместимость с программными модулями, разработанными до ведения параметризованных типов в стандарт Java. Проблема невозможности определения параметра типа на этапе компиляции решается передачей типа в конструктор создаваемого модуля сериализации.

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

Обработка сообщений

Средства рефлексии могут быть использованы в модуле, реализующем бизнес-логику приложения, при обработке полученных сообщений. Для поиска программного модуля, который отвечает за обработку полученных данных можно использовать аннотации, добавленные к методам класса-обработчика. Пример диаграммы, демонстрирующей реализацию модуля обработки сообщений, представлен на рисунке (рис. 4).

Диаграмма реализации модуля обработки сообщений
Рисунок 4 — Диаграмма реализации модуля обработки сообщений

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

Выводы

На основании анализа существующих программных решений, обеспечивающих сериализацию и десериализацию данных, разработанных для выполнения на платформе Java (Google Gson, XStream), встроенных средств сериализации платформы .NET, а также решений, обеспечивающих использование событийно-ориентированной архитектуры (Flask, PyQt) были определены преимущества использования метаданных в виде аннотаций Java при разработке программного обеспечения.

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

Приведенные в статье методы организации программного решения могут быть использованы в приложениях, написанных на других языках программирования высокого уровня, таких как C#, Python и др.

Список литературы

  1. Макконнелл С. Совершенный код. Мастер-класс / С. Макконнелл / Пер. с англ.— М.: Издательство Русская редакция, 2010. — 896 стр.: ил. 4
  2. Trail: The Reflection API (The Java™ Tutorials) [Electronic resourse] / Интернет-ресурс. — URL: https://docs.oracle.com/javase/tutorial/reflect/. &mdash Загл. с экрана.
  3. Vasian Cepa Representing Explicit Attributes in UML / Vasian Cepa, Sven Kloppenburg // 7th International Workshop on Aspect-Oriented Modeling: материалы VII международной конференции, 2 октября 2005г., г. Montego Bay, Jamaica.  , 2005.
  4. Type Erasure (The Java™ Tutorials > Learning the Java Language > Generics (Updated)) [Electronic resourse] / Интернет-ресурс. — URL: https://docs.oracle.com/javase/tutorial/java/generics/erasure.html. — Загл. с экрана.