воскресенье, 15 марта 2009 г.

Введение в OSGi. Знакомимся с "серебряной пулей" для разработки модульных приложений.


Как говорил небезызвестный герой небезызвестного произведения Н.В. Гоголя "Давненько не брал в руки шашек". Так и я - давненько не радовал своих читателей тематическими постами.

Последнюю неделю активно ковырял внутреннее устройство Eclipse, точнее то, что называется Eclipse as a platform. Не все знают, что Eclipse - это не IDE, это, прежде всего, платформа для разработки приложений различного профиля и уровня сложности. Вот об этом и хочется рассказать.

Что такое OSGi


Начнем рассмотрение снизу вверх. Начиная с версии 3.0 Eclipse перевели на новую основу. Имя ей - OSGi.

OSGi (Open Services Gateway Initiative) - спецификация динамической плагинной (модульной) шины для создания Java-приложений, разрабатываемая консорциумом OSGi Alliance. Круг применений данной спецификации довольно широк: изначально разрабатывалась для создания встроенных систем (в частности, для автомобилей BMW, также в разработке спецификации активно учавствует Siemens), но сейчас на базе OSGi строят многофункциональные десктоп приложения (например, Eclipse SDK) и Enterprise-системы (например, Naumen DMS). Последняя версия спецификации носит номер 4.1 и доступна вот здесь.


Основная идея фреймворка OSGi - все в системе есть плагины (в терминах OSGi - бандлы (bundles)). Основной способ взаимодействия между бандлами - сервисы: объекты, зарегистрированные в ядре системы с заявленными реализованными интерфейсами. Бандлы регистрируют сервисы для предоставления определенной функциональности другим бандлам. Помимо этого OSGi предоставляет механизм создания и обработки событий, управление импортом/экспортом java-пакетов и библиотек, набор класслоадеров, методы адресации ресурсов.

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

Понятие "динамическая шина" обозначает, что можно на лету, т.е. не перезапуская приложение, устанавливать, подключать, отключать и обновлять модули системы. Это очень удобно в частности для Enterprise приложений, где важен высокий аптайм.



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

Реализации с открытыми исходниками:

  • Knopflerfish - расширенная реализация OSGi R3;

  • Oscar - реализация OSGi R3;

  • Felix - продолжение проекта Oscar, разрабатывается Apache Foundation, реализация OSGi R3 и R4;

  • Equinox - реализация OSGi R4 от Eclipse Foundation.


Коммерческие реализации:

  • Connected Systems - HubTea Embedded Server;

  • Echelon - LONWORKS Bundle Deployment Kit;

  • Espial - Espial DeviceTop;

  • Samsung - Samsung Service Provider 3.1;

  • Siemens - RIO framework.


Основа OSGi - его величество бандл


Бандл платформы OSGi (OSGi bundle) содержит java-классы и другие ресурсы, которые вместе могут реализовывать некие функции, а также предоставлять сервисы и пакеты другим плагинам. Конструктивно бандл может быть либо каталогом, либо jar-архивом. Бандл, который содержит в себе фреймворк и управляет жизненным циклом других бандлов называется системным.

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

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

  • INSTALLED - успешно установлен;

  • RESOLVED - разрешены все зависимости. Бандлу доступны все Java-классы и те бандлы, от которых он зависит. Данное состояние показывает, что бандл готов к старту;

  • STARTING - бандл стартует. Метод BundleActivator.start() выполняется и пока не вернул значения;

  • ACTIVE - бандл успешно запустился. Метод BundleActivator.start() вернул значение;

  • STOPPING - останавливается бандл. Метод BundleActivator.stop() вызван, но пока не вернул значения;

  • UNINSTALLED - бандл не установлен (удален), соответственно он не может переходить в другие состояния. Жизненный цикл бандла завершен.


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



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

Информация о бандле находится в файле манифеста META-INF/MANIFEST.MF. В данном случае понятие манифеста совпадает с понятием манифеста jar-архива. Установка бандла производится либо другим бандлом (например, системным), либо с помощью специальных средств, предлагаемых конкретной реализацией спецификации OSGi.

Интерфейс объекта Bundle определяет следующие методы для доступа к заголовкам манифеста плагина:

  • getHeaders() возвращает объект Dictionary, который содержит заголовки манифеста плагина и их значения в виде пар "Заголовок - Значение". Значения локализованы согласно настроек в java.util.Locale#getDefault;

  • getHeaders(String) возвращает объект Dictionary, который содержит заголовки манифеста плагина и их значения в виде пар "Заголовок - Значение". Значения локализованы согласно указаной локали.


Для получения текущего состояния бандла используется метод getState().

Понятие контекста бандла


Объект контекста бандла (BundleContext) реализует взаимодействие между OSGi и установленными плагинами, представляя собой контекст выполнения бандла внутри платформы. Этот private-объект создаётся при запуске бандла и может использоваться в следующих целях:

  • установка новых бандлов в окружение OSGi;

  • получение информации о других установленных бандлах;

  • получение постоянной области хранения;

  • получение сервисных объектов зарегистрированных сервисов;

  • регистрация сервисов в платформе;

  • подписка на сообщения о событиях фреймворка или отключение от этой подписки (регистрация и отключение обработчиков событий - листенеров).


Объект BundleContext создается фреймворком при старте бандла и является аргументом при вызове метода BundleActivator.start(BundleContext).
Каждый плагин обладает своим уникальным контекстом. После того, как вызван метод stop(BundleContext), объект BundleContext больше не должен использоваться и его использование вызывает исключительную ситуацию.

Интерфейс BundleContext определяет методы, которые могут быть использованы для получения информации о плагинах, установленных в рамках платформы OSGi:

  • getBundle() - возвращает объект Bundle, ассоциированный с данным контекстом;

  • getBundles() - возвращает массив бандлов, в данный момент установленных во фреймворк;

  • getBundle(Long) - возвращает объект Bundle, соответствующий указанному идентификатору, или null, если объекта не обнаружено.


Также, интерфейс BundleContext предоставляет метод для получения информации о свойствах фреймворка - getProperty(String). Этот метод может использоваться для получения следующих свойств:

  • org.osgi.framework.version - версия спецификации фреймворка;

  • org.osgi.framework.vendor - поставщик реализации фреймворка;

  • org.osgi.framework.language - используемый язык;

  • org.osgi.framework.executionenvironment - разделённый запятыми список окружений выполнения (Execution Environment);

  • org.osgi.framework.processor - наименование используемого процессора. Примеры- x86 для Intel, Mips для SGI, Alpha для Compaq;

  • org.osgi.framework.os.version - версия операционной системы;

  • org.osgi.framework.os.name - наименование операционной системы.


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


Понравилось сообщение - подпишись на блог

З.Ы. Если Вы есть во Вконтакте - вступайте в группу Russian Eclipse Community.

14 комментариев:

Oleg комментирует...

Спасибо за статью, всё руки не доходят самому поразбираться.

Вы планируете цикл статей?

Unknown комментирует...

Здравствуйте.
Да, планирую написать цикл статей. Примерный план такой:
- Введение в OSGi
- Подробнее про ЖЦ бандла (если потребуется)
- Введение в Equinox. Equinox console.
- Введение в OSGi-сервисы
- Пример взаимодействия двух бандлов посредством сервисов
- Понятие Eclipse runtime (Eclipse core, Eclipse plugins)
- Точки расширения Eclipse, примеры
- Сравнение точек расширения Eclipse и сервисов OSGi
Может быть опубликую небольшое введение в Eclipse RPC.

А что вам интересно по данной теме?

Oleg комментирует...

Ваш план мне нравится.

Интересует практическая применимость на практике.

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

Мой опыт работы с шинами (ESB) показал, что очень часто(?) мы платим за такую архитектуру очень печальной производительностью системы. Особенно, если необходимо обрабатывать огромные объемы входных данных.

Unknown комментирует...

Да, думаю на этот вопрос сложно ответить однозначно. В Naumen выбрали OSGi, как средство построения легко расширяемых приложений на базе единой платформы. Удобно производить внедрение: грубо говоря требования конкретного клиента - отдельный бандл.

Однако я затрудняюсь понять, зачем на OSGi перешли в Eclipse.

Oleg комментирует...

Кстати, вот ещё туториал по теме - http://aneeshkumarkb.blogspot.com/

Unknown комментирует...

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

Но за ссылку все равно спасибо.

Alexander Lipatov комментирует...

Классные вебинары. Сейчас смотрю, испытываю что-то вроде восторга.

Анонимный комментирует...

Павел,

откуда у Вас информация о том, что OSGi используется BMW для своих автомобилей?

А.А.

Unknown комментирует...

Информация проскакивала внутри Naumen http://kernel.naumen.ru/Naumen_Kernel_Doc/OSGi/common_notes

Анонимный комментирует...

Павел,

есть ли у Вас источники с более подбробной информацией о BMW и OSGi? Может можно задать вопрос автору статьи на kernel.naumen.ru?

А.А.

Unknown комментирует...

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

Alex Ivanov комментирует...

Если вопрос BMW & OSGi все еще актуален, начать можно отсюда http://www.osgi.org/Markets/Automotive

Unknown комментирует...

Читаю Ваш блог и понимаю, что мне нравится OSGI, я даже думаю попробовать написать на его основе какой-нибудь реальный проект, скажите пожалуйста, есть ли какие-то проблемы у OSGI, какие-нибудь подводные камни? Вообще можете ли вы описать основные минусы OSGI в целом и Equinox как его реализации в частности, которые не видны на первоначальных этапах?

Unknown комментирует...

Я сам с OSGi сейчас уже давно не работаю, хотя вижу, что система развивается и идет вперед. Принимаются новые версии спецификации. Можно посмотреть OSGi для распределенных систем (Eclipse Communication Framework), можно - сервер приложений Eclipse Virgo. Ну и вообще успех проекта Eclipse говорит сам за себя.

Не скажу минусы это или нет, но технология реально сложна. Т.е. программирование отличается от обычного Java или Java EE. Неплоский ClassPath вносит свои коррективы. Зависимости между классами становятся сложнее и отследить их не всегда просто. Интеграция со многими библиотеками не доделана. Точнее библиотеки не хотят идти в сторону модульности. Простые примеры: Hibernate (JPA) и log4j (хотя это возможно исправлено в Log4j 2). Если сделать так, что мэпинги для Hibernate подключаются из бандлов можно, то вот сделать мэпинги подключаемыми и отключаемыми во время исполнения приложения уже нельзя. И уж тем более горячую замену мэпингов. Hibernate (по крайней мере третий) на это не рассчитан. Вы один раз создаете SessionFactory и все. Отсюда теряются такие "коренные" преимущества OSGi как замена кода во время исполнения. Аналогично с логированием: даже сделать подключаемые в модулях настройки логов уже не так то просто.

Oracle обещал в Java 9 новую модульную технологию - jigsaw. Не знаю насколько она будет успешной и будет ли вообще. Но возможно для поддержки технологии "из коробки" разработчикам библиотек придется тратить время и силы.

Отправить комментарий

Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!