OSGi описание, пример

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

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

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

Компонентная платформа OSGi призвана решить вышеуказанные требования, а также и некоторые другие.

OSGi (Open Services Gateway Initiative) представляет собой спецификацию динамической модульной системы и сервисной платформы для Java-приложений, разрабатываемую консорциумом OSGi Alliance. Данная спецификация определяет модель построения приложения из компонентов, которая динамически может связывать различные модули. Состав компонентов может изменяться во время выполнения приложения (run-time). Взаимодействие между компонентами осуществляется с помощью сервисов, которые зарегистрированы в регистре сервисов (Service Register).

Таким образом, технология OSGi определяет сервис-ориентированную платформу построения приложений с поддержкой модульности.

Платформа OSGi оперирует модулями, называемыми бандлами (bundle). Каждый bundle в OSGi – это логически завершённый программный компонент, реализующий определенный функционал. Платформа OSGi позволяет управлять модульным приложением, предоставляя ему следующие возможности :

  1. Подключение модулей в систему. Подключить bundle можно как на этапе сборки приложения, так и во время работы приложения.
  2. Разрешение зависимостей модулей с учётом версий. В OSGi приложении каждый модуль имеет свой номер версии, благодаря чему другие модули могут заявлять зависимость от конкретной версии модуля или от некоторого диапазона версий. Следует отметить, что одновременно могут работать несколько версий одного модуля.
  3. Управление модулями. Добавленные в систему bundle, могут быть запущены и остановлены без перезапуска всей системы. Сервисы, предоставляемые запущенным модулям, доступны другим модулям. При запуске и останове bundle платформа OSGi отслеживает зависимости модулей друг от друга и от сервисов, предоставляемых модулями. В случае останова модуля, предоставляющего некоторый сервис, зависимые от него модули также будут остановлены, или, если другой запущенный модуль предоставляет аналогичный сервис, зависимые модули получат другой экземпляр сервиса из запущенного модуля.
  4. Управления пользователями (User Admin Service). Данный механизм предоставляет интерфейсы для аутентификации и авторизации пользователей в приложении, а также обеспечивает возможность создания пользователей, ролей, групп пользователей. Модули OSGi, поддерживающие авторизацию пользователей через User Admin Service, проверяют наличие у пользователя прав на выполнение тех или иных действий.
  5. Протоколирование событий. Модули OSGi получают доступ к общему журналу приложения (Log Service) для регистрации различного рода сообщений.
  6. Механизм конфигурирования модулей (Configuration Admin Service). Приложение можно гибко настраивать, подключая и конфигурируя сервисы.
  7. Сервис свойств модулей (Preferences Service). Внутренние параметры модулей можно хранить в постоянном хранилище, предоставляемом платформой OSGi. После перезапуска всего приложения данные хранилища будут доступны в неизменном виде.
  8. Сервис HTTP (HTTP Service). Приложение OSGi может обрабатывать web-запросы. Данный сервис позволяет модуль зарегистрировать в качестве обработчика HTTP-запросов. HTTP Service представляет собой web-сервер, вызывающий зарегистрированные OSGi модули в соответствии с полученным запросом.

Спецификация OSGi определяет наряду с вышеперечисленными также множество других стандартных сервисов, которые вместе составляют набор сервисов OSGi (OSGi Service Compendium). Благодаря открытости платформы OSGi, использование bundle, предоставляющих приложению всевозможные сервисы, является простой задачей.

Реализации платформы OSGi

Спецификация платформы OSGi описывает интерфейсы и поведение стандартных сервисов, предоставляемых платформой. OSGi разрабатывается с 1999-го года и имеет несколько реализациий :

  • Equinox - реализация OSGi R4 от Eclipse Foundation. Лежит в основе среды разработки Eclipse IDE. Эта реализация является одной из наиболее полных, поскольку содержит все необходимые и большинство необязательных сервисов из спецификации платформы OSGi.
  • Knopflerfish - расширенная реализация OSGi R3. В комплект включена графическая консоль для управления модулями.
  • Apache Felix - реализация OSGi R3 и R4 от Apache Foundation. Предоставляет веб интерфейс для управления и просмотра состояния платформы через браузер.
  • Concierge OSGi - эта реализация платформы OSGi предназначена для работы в условиях ограниченных ресурсов (например, в мобильных и встраиваемых устройствах).

Модули платформы OSGi, bundle

В терминологии OSGi модуль, называемый bundle, представляет собой обычный JAR-архив (Java ARchive). Здесь необходимо отметить, что при разработке java приложений, как правило, используются различные библиотеки, представляющие собой, также JAR-архивы. Использование подобных библиотек может быть сопряжено с определенными сложностями.

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

Еще одной проблемой разработчиков является так называемый JAR Hell, о который многие програмисты сломали немало копий. Суть этой проблемы состоит в том, что как только в проекте начинают использоваться разные версии одной и той же библиотеки, то можно столкнуться с ситуацией, когда один и тот же класс имеет разные методы в разных версиях библиотеки. Java же устроена так, что будет использована первая версия библиотеки, которую найдет загрузчик классов. Тем самым, обратившись в коде к какому-либо классу во время выполнения программы, можно неожиданно получить ошибку, что метод, к которому Вы обращаетесь, не существует. Это связано с тем, что на этапе выполнения JVM ничего не знает о версии библиотеки, которая должна использоваться в том или ином случае.

Отличие bundle от библиотеки JAR

Стоит отметить, что разработчики OSGi решили не изменять структуру JAR-файлов для обеспечения модульности, а просто включили в них дополнительную информацию (в файл META-INF/MANIFEST.MF), которая используется средой OSGi. Эта дополнительная информация никак не влияет на использование JAR-файлов в обычных Java-приложениях. Итак, чтобы JAR-файл стал OSGi-компонентом, в него добавляются данные, которые определяют, какие пакеты данного набора доступны для использования вне его (Export-Package) и какие пакеты других наборов требуются для работы этого набора (Import-Package). При этом возможно задать как версию API, которую набор предоставляет для других наборов, так и версию или диапазон версий API, которые набор требует для своей работы от них же. Все классы набора, которые не располагаются в его экспортируемой секции, не доступны вне набора (Private). Таким способом OSGi-компонент выполняет требование слабой связности.

Так как зависимости между компонентами и интерфейсы, предоставляемые этими компонентами, имеют четкие версии, среда выполнения OSGi позволяет легко избегать ситуаций JAR Hell.

Немного расширив описание обычного JAR-файла и добавив поддержку этого описания в среду выполнения, OSGi-сообщество решило проблему создания модулей в Java. Среда выполнения OSGi позволяет динамически загружать и выгружать компоненты во время выполнения. Более того, OSGi отслеживает зависимости между компонентами и динамически разрешает их.

Компоненты OSGi bundle выполняют определенные функции и включают :

  • классы и ресурсы, в которых реализована определенная функциональность;
  • класс активатор (может отсутствовать);
  • файл META-INF/MANIFEST.MF, в котором определяются параметры bundle.

Согласно спецификации OSGi для bundle, реализующего интерфейс BundleActivator, определен следующий жизненный цикл (Lifecycle) :

INSTALLEDуспешно установлен;
RESOLVEDbundle готов к старту и ему доступны все Java-классы и бандлы, от которых он зависит;
STARTINGbundle стартует, т.е. выполняется метод BundleActivator.start();
ACTIVEbundle стартовал, т.е метод BundleActivator.start() завершился;
STOPPINGbundle останавливается, т.е. выполняется метод BundleActivator.stop();
UNINSTALLEDbundle не установлен (удален), т.е. жизненный цикл бандла завершен.

Схема жизненного цикла bundle взята из спецификации OSGi.

Дополнительные метаданные OSGi компонента

OSGi-компонент (bundle) должен включать дополнительные метаданные в файле META-INF/MANIFEST.MF. Некоторые из основных представлены ниже :

  • Bundle-Name — имя модуля;
  • Bundle-Version — версия модуля, по-умолчанию 0.0.0;
  • Bundle-SymbolicName — символьное имя модуля, совместно с версией служат уникальным идентификаторов модуля;
  • Export-Package — список экспортируемых Java-пакетов модуля с возможными дополнительными директивами;
  • Import-Package — список импортируемых Java-пакетов используемых в модуле с возможными дополнительными директивами.

Взаимодействие OSGi бандлов

Согласно спецификации OSGi можно использовать несколько способов взаимодействия бандлов :

  1. Использование ресурсных бандлов. Ресурсные бандлы позволяют определять централизованное хранилище данных типа изображений images, свойств properties, которые могут быть использованы для локализации и настройки интерфейса системы.
  2. Использование сервисных бандлов. Наименование спецификации OSGi уже определяет взаимодействия модулей посредством сервисов. Сервисные бандлы регистрируются и активируются в контейнере, после чего выступают в роли «сервера», к которым подключаются другие бандлы, выполняющие роль «клиента», для получения соответствующих услуг (сервисов).
  3. Использование механизма «публикация-подписка», которая представляет стандартную событийную модель. Данный подход предсматривает, что одни бандлы должны зарегистрировать определенные события, а другие, заинтересованные бандлы должна на них «подписаться». При возникновении зарегистрированного события в одном бандле сразу же вызывается его обработчик в другом.

Пример создания OSGi bundle

Рассмотрим простой пример создания OSGi bundle в IDE Eclipse. Структура maven проекта osgi.hello представлена на следующем скриншоте.

OSGi bundle, как было отмечено выше, должен реализовывать интерфейс BundleActivator. Кроме этого, bundle должен включать при необходимости методы start и stop, чтобы контейнер мог стартовать и остановить bundle. В качестве параметра контейнер передает этим методам BundleContext, который позволяет модулю взаимодействовать с контейнером. В следующем листинге (Activator.java) представлен исходный код bundle :

package com.osgi.sample;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator
{
    public void start(BundleContext context) throws Exception {
        System.out.println("Start plugin activator");
    }
    public void stop(BundleContext context) throws Exception {
        System.out.println("Stop plugin activator");
    }
}

Для сборки bundle в файл конфигурации pom.xml включен плагин maven-bundle-plugin от разработчиков Apache Felix, который использует (знаменитую в OSGi кругах) утилиту Bnd. Этот плагин конфигурируем следующим образом :

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.osgi.sample</groupId>
  <artifactId>osgi.hello</artifactId>
  <packaging>bundle</packaging>
  <version>1.0.0</version>

  <name>OSGI Demo Bundle</name>
  <url>http://maven.apache.org</url>

  <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <dependencies>
      <dependency>
          <groupId>org.apache.felix</groupId>
          <artifactId>org.osgi.core</artifactId>
          <version>1.4.0</version>
      </dependency>
  </dependencies>
  
  <build>
      <plugins>
          <plugin>
              <groupId>org.apache.felix</groupId>
              <artifactId>maven-bundle-plugin</artifactId>
              <version>2.3.7</version>
              <extensions>true</extensions>
              <configuration>
                  <instructions>
                     <Bundle-SymbolicName>
                         ${project.artifactId}
                     </Bundle-SymbolicName>
                     <Bundle-Name>
                         ${project.name}
                     </Bundle-Name>
                     <Bundle-Version>
                         ${project.version}
                     </Bundle-Version>
                     <Bundle-Activator>
                         ${project.groupId}.Activator
                     </Bundle-Activator>
                     <Private-Package>
                         ${project.groupId}
                     </Private-Package>
                  </instructions>
              </configuration>
          </plugin>
      </plugins>
  </build>
</project>

Следует обратить внимание на то, что наименование записей в секции instructions совпадают с наименованиями специфических заголовков для метаданных OSGi-модуля в META-INF/MANIFEST.MF. Это они и есть, только не в своём натуральном виде, а в виде инструкций bnd. Данная утилита достаточно интеллектуальна и умеет вычислять зависимости по коду и подставлять их в секцию Import-Package, а также добавлять различные метаданные. На основе своей конфигурации и вычислений она генерирует MANIFEST.MF :

Листинг манифеста MANIFEST.MF

Manifest-Version: 1.0
Bnd-LastModified: 1485887772941
Build-Jdk: 1.7.0_67
Built-By: Kernel
Bundle-Activator: com.osgi.sample.Activator
Bundle-ManifestVersion: 2
Bundle-Name: OSGI Demo Bundle
Bundle-SymbolicName: osgi.hello
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.osgi.sample;uses:="org.osgi.framework";version="1.0.0"
Import-Package: org.osgi.framework;version="[1.5,2)"
Tool: Bnd-1.50.0

Контейнер OSGi

Консоль OSGi позволяет из командной строки получить доступ к OSGi контейнеру и выполнить ряд команд по инсталляции, старту и останову bundle. Чтобы открыть консоль OSGi при установленном на компьютере IDE Eclipse необходимо в командой строке войти в директорию <ECLIPSE_HOME>/plugins и выполнить следующую команду :


java -jar org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar -console
 

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

Примечание : в команде используется версия 3.7.1.R37x_v20110808-1106, включаенная в IDE Eclipse Indigo. У Вас может быть другая версия.

После выполнения этой команды на экране появится приглашение osgi > . Теперь можно вводить команды для управления контейнером. Для просмотра инициализированных в контейнер модулей необходимо выполнить команду ss.


osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.7.1.R37x_v20110808-1106
 

Теперь можно наш bundle инсталлировать в контейнер и стартовать :


osgi> install file:F:/osgi.container/bundles/osgi.hello-1.0.0.jar
Bundle id is 19

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.7.1.R37x_v20110808-1106
19      INSTALLED   osgi.hello_1.0.0

osgi> start 19
Start plugin activator

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.7.1.R37x_v20110808-1106
19      ACTIVE      osgi.hello_1.0.0
 

Команды управления жизненым циклом bundle

installустановка bundle в контейнере (параметром команды является URL)
uninstallудаление bundle из контейнера
startзапуск bundle
stopостановка bundle
refreshобновление пакетов в bundle
updateобновление bundle

Чтобы увидеть всю справочную информацию org.eclipse.osgi_ХХХ.jar используйте команду «help».

Скачать исходный код

Исходные коды рассмотренного примера создания bundle для платформы OSGi в виде проекта Eclipse можно скачать здесь (1.23 Мб). Архив проекта дополнительно содержит директорию osgi.container, включающую org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar для создания контейнера Equinox и тестирования бандла.

Пример создания bundle-сервиса, регистрация его в контейнере OSGi и взаимодействие bundle-сервиса с bundle-клиентом представлен на странице Cервисы взаимодействия бандлов.

  Рейтинг@Mail.ru