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

В этой статье мы рассмотрим две самые популярные реализации OSGi – Equinox и Felix, – научимся создавать бандлы в Eclipse IDE, устанавливать их, запускать, останавливать и удалять.

Знакомство с контейнерами OSGi

Все приложения, в основе которых лежит OSGi, запускаются в OSGi-контейнерах (иногда их еще называют OSGi-фреймворками). Существует несколько открытых (open source) и коммерческих OSGi-контейнеров:

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

Далее мы устроим для них тест-драйв.

Eclipse Equinox

Наверно каждый, кто программирует на Java, уже использовал Equinox сам того не подозревая, ведь он лежит в основе такой популярной среды разработки как Eclipse IDE. Каждый Eclipse-плагин является OSGi-бандлом.

Для экспериментов нам потребуется фреймворк Equinox. Скачать его можно со страницы http://download.eclipse.org/eclipse/equinox/. Скачивать будем Latest Release. На момент написания статьи это версия 3.5.1. Переходим по ссылке и видим несколько таблиц. Нас интересует Framework. Его объем – 1.1 MB.

Чтобы запустить Equinox, нужно в командном окне перейти в каталог с только что скачанным JAR-файлом и выполнить команду java -jar org.eclipse.osgi_3.5.1.R35x_v20090827.jar, при этом подразумевается что у вас уже установлен пакет JDK версии не меньше 1.5 и правильно установлены переменные окружения.

После запуска Equinox, возможно возникнет небольшая пауза, вслед за чем вы снова увидите приглашение командной строки. Запустился ля Equinox? И да и нет…

Equinox запустился. Однако он ни чего не сделал и незамедлительно завершил свое выполнение. Чтобы перейти в режим командной строки Equinox и управлять им, нужно немного изменить команду запуска:

java -jar org.eclipse.osgi_3.5.1.R35x_v20090827.jar -console

После выполнения этой команды вы должны увидеть приглашение командной строки Equinox:

osgi>

Если вы видите приглашение Equinox, значит он “жив” и ждет ваших указаний. Выполнив команду help, вы можете ознакомиться со списком команд, которые позволяю управлять фреймворком и вашими бандлами.

Давайте попробуем выполнить несколько наиболее полезных команд. Выполните команду ss. Вы должны увидеть следующий вывод:

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.5.1.R35x_v20090827

osgi>

Команда ss – сокращение от short status, – выводит все установленные (installed) бандлы в OSGi-контейнере. В нашем случае установлен только один бандл (сам Equinox, который тоже является бандлом). Из вывода команды ss мы можем узнать, что бандл Equinox имеет идентификатор 0, находится в активном (ACTIVE) состоянии и имеет имя org.eclipse.osgi_3.5.1.R35x_v20090827.

В первом приближении упоминался файл MANIFEST.MF, который хранит специальные заголовки и должен присутствовать в каждом OSGi-бандле. Когда мы сами разрабатываем бандл, то мы знаем или в любой момент можем посмотреть какие заголовки содержит наш MANIFEST.MF, но что делать если вы используете бандл стороннего разработчика? Чтобы просмотреть заголовки любого OSGi-бандла, выполните команду headers с идентификатором бандла. Например, чтобы просотреть заголовки бандла Equinox, выполните следующую команду:

osgi> headers 0

В результате её выполнения, вы получите массу информации об Equinox, например о том, что авторским правом на этот бандл обладает IBM и что он экспортирует несколько десятков пакетов, в том числе и пакеты OSGi Alliance (org.osgi.*). Эти пакеты мы можем импортировать в своих бандлах и использовать предоставляемую ими функциональность.

Более близко мы познакомимся с Equinox когда будем писать свой бандл, а сейчас, прежде чем рассмотреть Felix, давайте выполним еще одну не маловажную команду:

osgi> exit

Equinox – очень мощный OSGi-контейнер, но он распространяется под лицензией Eclipse Public License (EPL), которая не совместима, например, с GNU General Public License. Если совместимость лицензий имеет большое значение, то есть альтернатива Equinox, которая распространяется под лицензией Apache License 2.0. Имя ей – Apache Felix.

Apache Felix

Для будущих экспериментов нам понадобится Apache Felix. Во время написания статьи, последней версией была 2.0.1. Cкачайте felix-framework-2.0.1.zip или felix-framework-2.0.1.tar.gz и распакуйте. Затем запустите командную строку, перейдите в директорию <felix_root> и выполните следующую команду, чтобы запустить Felix:

java -jar bin\felix.jar

В результате вы должны увидеть приглашение Felix:

Welcome to Felix
================

->

Теперь мы можем увидеть список команд, которые ожидает от нас Felix. Для этого нужно выполнить команду help:

-> help
bundlelevel
cd
find
headers
help
inspect
install
log
obr
ps
refresh
resolve
shutdown
start
startlevel
stop
sysprop
uninstall
update
version

Use ‘help ‘ for more information.
->

Первое, что бросается в глаза – набор команд Felix намного меньше, чем у Equinox, однако не стоит переживать, Felix имеет все необходимые команды для полноценной работы с технологией OSGi. Чтобы посмотреть список установленных бандлов, выполните команду ps:

-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
->

Вывод команды ps говорит нам о том, что базовая конфигурация Felix включает в себя четыре бандла. В большинстве случаев, разработчикам не обязательно знать, что делают эти бандлы, но тем не менее такая возможность существует. Давайте, например, выведем на экран заголовки манифеста System Bundle. Для этого выполните команду headers 0.

-> headers 0
System Bundle (0)
-----------------
Bundle-Description = This bundle is system specific; it implements various system services.
Bundle-ManifestVersion = 2
Bundle-Name = System Bundle
Bundle-SymbolicName = org.apache.felix.framework
Bundle-Version = 2.0.1
Export-Package = org.osgi.framework; version="1.5.0"
...

В заголовке Bundle-Description хранится краткая информация о том, что делает данный бандл. В данном случае, System Bundle реализует различные системные сервисы. Аналогичным образом можно узнать за что отвечают еще три системных бандла Felix: бандл Apache Felix Bundle Repository предоставляет сервисы репозитория бандлов, бандл Apache Felix Shell Service предоставляет командную оболочку для Felix, а бандл Apache Felix Shell TUI предоставляет текстовый пользовательский интерфейс для shell-сервиса.

Чтобы закончить работу с Felix, выполните команду shutdown.

Мы научились пользоваться двумя самыми популярными OSGi-контейнерами, давайте теперь разработаем бандл Hello World и запустим его в OSGi-контейнере.

Hello, OSGi!

Вы уже знаете, что OSGi-бандл – это нечто большее, чем просто JAR. OSGi-бандл – это JAR с манифестом META-INF/MANIFEST.MF. Давайте создадим OSGi-бандл с помощью Eclipse IDE и поработаем с его манифестом.

Для разработки я буду использовать Eclipse RCP 3.5.1. Итак, приступим.

Откройте мастера создания нового Plug-in проекта с помощью меню File -> New -> Other… или комбинацией Ctrl + N, раскройте узел Plug-in Development, выберите Plug-in Project и нажмите Next. Введите имя проекта, например ru.topcode.hello, и в блоке Target Platform установите радиокнопку в положение an OSGi framework, при этом в выпадающем списке должен быть выбран Equinox. Нажми Next, чтобы перейти на страницу настройки плагина.

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

Создание OSGi-бандла

Создание OSGi-бандла

Нажав на кнопку Next, вы попадете на страницу Templates, на которой можно выбрать шаблон OSGi-бандла, но мы откажемся от этой услуги и создадим бандл сами. Для этого снимите флаг Create a plug-in using one of the templates и нажмите Finish. На этом создание нашего первого бандла завершено. Его структура очень проста: в src лежит пакет, который мы указали на этапе создания (ru.topcode.hello), в этом пакете лежит единственный класс Activator.java, который мы рассмотрим ниже, рядом с src находится каталог META-INF с файлом MANIFEST.MF, который и является манифестом бандла. Если редактор манифеста еще не открыт, откройте его и перейдите на страницу MANIFEST.MF.

Здесь хранятся заголовки бандла.

Bundle-ManifestVersion
Этот заголовок определен в спецификации OSGi и используется при чтении бандла. Как ни странно, значение по умолчанию, 1.0, означает третий релиз OSGi, а значение 2.0 – указывает на OSGi Release 4 и выше. Запутанно? Еще бы! Здесь просто нужно запомнить, что пока вы используете OSGi 4.x, значением этого заголовка будет 2.0.
Bundle-SymbolicName
Это единственный обязательный заголовок, который является уникальным идентификатором бандла. Значение может быть практически любым, но чтобы гарантировать уникальность, рекомендуется использовать правила именования пакетов, т.е. использовать записанное наоборот доменное имя.
Bundle-Name
Этот заголовок содержит человеко-понятное имя, в котором могут использоваться практически любые символы, однако не рекомендуется использовать пробелы.
Bundle-Version
В этом заголовке хранится версия бандла. О версионировании бандлов мы поговорим позже, скорей всего в следующих постах.
Bundle-Activator
В заголовке Bundle-Activator хранится полное квалифицированное имя класса, отвечающего за жизненный цикл бандла. Этот класс называется активатором, реализует интерфейс BundleActivator и его методы start() и stop(), которые вызываются при запуске и останове бандла.
Import-Package
Этот заголовок содержит один или более пакетов (находящихся в других бандлах), которые требуются данному бандлу для работы. Например, активатор бандла использует классы из пакета org.osgi.framework, поэтому мы (в данном случае Eclipse, но мы ведь можем писать манифест и в блокноте) импортируем этот пакет, прописывая его имя в Import-Package.

Откройте теперь класс Activator.

Активатор бандла – это специальный класс, который вызывается при запуске и останове бандла, принимая тем самым непосредственное участие в жизенном цикле этого бандла. Используя активатор, мы можем запрограммировать поведение бандла при запуске и останове. Класс Activator реализует интерфейс org.osgi.framework.BundleActivator, который определяет методы start() и stop(), которые вызываются когда бандл стартует или останавливается соответственно.

Измените класс Activator следующим образом (комментарии удалены в целях экономии места):

package ru.topcode.hello;
 
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("Hello World!");
	}
 
	public void stop(BundleContext context) throws Exception {
		System.out.println("Goodbye World!");
	}
}

В этом примере мы определили, что при запуске бандла, в консоль будет выводиться строка “Hello World!”, а при останове – “Goodbye World!”. Заметьте, что оба метода получают BundleContext в качестве аргумента. Этот аргумент может использоваться для взаимодействия с OSGi-контейнером. Т.к. в этом примере взаимодействие с контейнером не предполагается, мы рассмотрим этот момент немного позже.

Теперь всё готово для запуска бандла, однако сначала нужно собрать его. Для этого откройте MANIFEST.MF и нажмите на кнопкуExport deployable plug-ins and fragments (export-plugins-action).

В появившемся диалоговом окне на вкладке Destination установите каталог, в который экспортируется JAR-файл. Всё остальное можно оставить как есть и нажать на Finish.

В результате экспорта мы получили JAR-файл с именем ru.topcode.hello_1.0.0.1.jar. Теперь необходимо установить этот бандл в OSGi-контейнер. Для тестирования бандла я буду использовать контейнер Equinox.

Запустите OSGi-контейнер Equinox:

java -jar org.eclipse.osgi_3.5.1.R35x_v20090827.jar -console

Бандл Hello у меня находится в каталоге target, который расположен в каталоге с OSGi-контейнером org.eclipse.osgi_3.5.1.R35x_v20090827.jar. Чтобы установить бандл в OSGi-контейнер, выполните следующую команду:

install file:target/ru.topcode.hello_1.0.0.1.jar

В ответ на эту команду вы получите идентификатор, который присвоен бандлу в OSGi-контейнере:

Bundle id is 1

Выполните теперь команду ss чтобы посмотреть на список бандлов контейнера. В этом списке вы должны увидеть наш бандл ru.topcode.hello_1.0.0.1 с id = 1 и состоянием INSTALLED. Таким образом, бандл уже установлен в контейнере, но пока что он не готов к работе. Чтобы запустить его, выполните команду start с идентификатором бандла:

start 1

В ответ на эту команду, вы должны получить вывод в консоль строки “Hello World!”. Это сработал метод start() класса Activator нашего бандла. Снова выполните команду ss и вы увидите что наш бандл теперь находится в состоянии ACTIVE.

Всё это отлично, но мы увидели лишь часть того, что запрограммировали. Выполните теперь команду

stop 1

и убедитесь, что в консоль выводится строка “Goodbye World!”. Если вы снова выполните команду ss, то увидите, что бандл перешел в состояние RESOLVED. Это означает, что наш бандл больше не активен в этом контейнере.

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

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