Ресурсные бандлы OSGi

Структура bundle с примером представлена на странице описания спецификации динамической модульной системы OSGi (Open Services Gateway Initiative). В спецификации OSGi модуль именуют как bundle. В этой статье будет рассмотрен пример создания bundle-ресурса, который будет использоваться другим бандлом. В качестве ресурса включим в бандл объект локализации интерфейса приложения Locale. В качестве общих ресурсов модулей системы могут быть использованы изображения Image, свойства Properties и т.д.

Пример использования бандла-ресурсов

При разработке и тестировании примера с бандлом ресурсов будут созданы два maven-проекта :

  • bundle-resource : модуль, хранящий объект локализации интерфейса Locale;
  • bundle-locale : модуль, использующий bundle-resource.

Описание bundle-resource

Bundle ресурсов создает и хранит объект Locale. Проект модуля ресурсов bundle-resource включает интерфейс IResource.java, реализацию ResourceImpl.java и активатор ResourceActivator.java.

Листинг интерфейса IResource.java

import java.util.Locale;

public interface IResource 
{
    public Locale getLocale();
    public void   setLocale(final String lang);
}

Интерфейс включает методы чтения и записи (get/set) объекта локализации Locale.

Листинг класса ResourceImpl.java

import java.util.Locale;

public class ResourceImpl implements IResource
{
    private static Locale locale;

    public Locale getLocale()
    {
        return locale;
    }
    public void setLocale(final String lang)
    {
        locale = new Locale(lang);
    }
}

Класс ResourceImpl реализует методы интерфейса IResource и включает статический объект locale, который создается в методе setLocale. Данный метод позволяет определить язык локализации системы в режиме run-time.

Листинг активатора бандла ресурсов ResourceActivator.java

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

public class ResourceActivator implements BundleActivator 
{
    private static int start_count = 0;
    private String[]   locales = {"ru", "de", "en"};

    public void start(BundleContext context) throws Exception 
    {
        int idx = start_count % 3;
        start_count++;
        new ResourceImpl().setLocale(locales[idx]);
        System.out.println("start bundle-resource");
    }

    public void stop(BundleContext context) throws Exception
    {
        System.out.println("stop bundle-resource");
    }
}

Активатор бандла при старте (метод start) получает в качестве параметра контекст контейнера context типа BundleContext и определяет объект локализации интерфейса Locale. Язык локализации определяется согласно значению счетчика стартов бандла start_count, т.е. при каждом старте значение счетчика увеличивается и для Locale выбирается очередное значение массива locales. В методе stop освобождаются ресурсы bundle.

Описание сборки pom.xml

Для сборки проекта bundle-resource используется pom.xml следующего содержания :

<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.resources</groupId>
  <artifactId>bundle-resource</artifactId>
  <packaging>bundle</packaging>
  <version>1.0.0</version>

  <name>Module resources</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.apache.felix.framework</artifactId>
          <version>5.6.1</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.groupId}.resource
                      </Bundle-SymbolicName>
                      <Bundle-Name>
                             ${project.name}
                      </Bundle-Name>
                      <Bundle-Version>
                             ${project.version}
                      </Bundle-Version>
                      <Bundle-Activator>
                             ${project.groupId}.ResourceActivator
                      </Bundle-Activator>
                      <Export-Package>
                          com.osgi.resources
                      </Export-Package>
                  </instructions>
              </configuration>
          </plugin>
      </plugins>
  </build>
</project>

В файле описания сборки pom.xml определяется зависимость фреймворка osgi (секция <dependency>) и параметры bundle для формирования манифеста META-INF/MANIFEST.MF. В тег <Export-Package> записываем наименование пакета. В результате сборки в поддиректории проекта target будет создан bundle в виде файла bundle-resource-1.0.0.jar с манифестом следующего содержания :

Manifest-Version: 1.0
Bnd-LastModified: 1493449322857
Build-Jdk: 1.8.0_121
Bundle-Activator: com.osgi.resources.ResourceActivator
Bundle-ManifestVersion: 2
Bundle-Name: Module resources
Bundle-SymbolicName: com.osgi.resources.resource
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.osgi.resources;\
                uses:="org.osgi.framework";version="1.0.0"
Import-Package: org.osgi.framework;version="[1.8,2)"
Tool: Bnd-1.50.0

Описание bundle-locale

Структура плагина bundle-locale включает только активатор LocaleActivator.java.

Листинг активатора LocaleActivator.java

import java.util.Locale;

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

import com.osgi.resources.IResource;
import com.osgi.resources.ResourceImpl;

public class LocaleActivator implements BundleActivator
{
    public void start(BundleContext context) throws Exception
    {
        System.out.println("start bundle-locale");
        IResource resources = new ResourceImpl();
        Locale locale = resources.getLocale();
        if (locale == null)
            System.out.println("locale = null");
        else
            System.out.println("Locale : " +
                               "language = " + locale.getLanguage() + 
                               ", name = " + locale.getDisplayName());
    }

    public void stop(BundleContext context) throws Exception
    {
        System.out.println("stop bundle-locale");
    }
}

Бандл bundle-locale при старте получает объект локализации locale и выводит информацию в консоль.

Описание сборки pom.xml

<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.bundle</groupId>
  <artifactId>bundle-exchange</artifactId>
  <packaging>bundle</packaging>
  <version>1.0.0</version>

  <name>Bundle locale</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.apache.felix.framework</artifactId>
          <version>5.6.1</version>
      </dependency>
      <dependency>
          <groupId>com.osgi.resources</groupId>
          <artifactId>bundle-resource</artifactId>
          <version>1.0.0</version>
          <scope>provided</scope>
      </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.groupId}.locale
                     </Bundle-SymbolicName>
                     <Bundle-Name>
                             ${project.name}
                     </Bundle-Name>
                     <Bundle-Version>
                             ${project.version}
                     </Bundle-Version>
                     <Bundle-Activator>
                            ${project.groupId}.LocaleActivator
                     </Bundle-Activator>
                     <Import-Package>
                          org.osgi.framework.*,
                          com.osgi.resources
                     </Import-Package>
                  </instructions>
              </configuration>
          </plugin>
      </plugins>
   </build>
</project>

В файле описания сборки pom.xml определяется зависимость плагина от артифакта service-resource на время разработки (provided), необходимая для компиляции и создания bundle. Чтобы maven нашел данный артифакт в репозитории необходимо выполнить в предыдущем проекте bundle-resource команду mvn install, которая загрузит артифакт в локальный репозиторий. В секции Import-Package необходимо дополнительно определить используемые бандлом пакеты.

В результате сборки в поддиректории проекта target будет создан bundle в виде файла bundle-locale-1.0.0.jar с манифестом следующего содержания :

Manifest-Version: 1.0
Bnd-LastModified: 1493449570534
Build-Jdk: 1.8.0_121
Bundle-Activator: com.osgi.bundle.LocaleActivator
Bundle-ManifestVersion: 2
Bundle-Name: Bundle locale
Bundle-SymbolicName: com.osgi.bundle.locale
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.osgi.bundle;uses:="org.osgi.framework,\
                         com.osgi.resources";version="1.0.0"
Import-Package: com.osgi.resources;version="[1.0,2)",\
                         org.osgi.framework;version="[1.8,2)"
Tool: Bnd-1.50.0

Тестирование

Как было отмечено выше, в архив примеров входит папка «osgi.container», в которой размещается org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar для создания контейнера Equinox. Для автоматической загрузки плагинов (бандлов) их необходимо разместить в поддиректории «osgi.container/bundles», а в поддиректории «osgi.container/configuration» настроить файл инициализации config.ini следующим образом :

osgi.bundles=bundles/bundle-resource-1.0.0.jar@start, \
bundles/bundle-locale-1.0.0.jar@start

После этого при создании контейнера Equinox бандлы будут загружаться и стартовать автоматически. А в консоли мы увидим следующую «картину» :


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

osgi> 
start bundle-resource
start bundle-locale
Locale : language = ru, name = Russian
ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.7.1.R37x_v20110808-1106
1       ACTIVE      com.osgi.resources.locale_1.0.0
2       ACTIVE      com.osgi.bundle.locale_1.0.0
 

При инициализации бандлов bundle-resource создал Locale с кириллицей RU_ru, информацию о которой в консоль вывел bundle-locale. Поскольку при каждом перестарте создается новый объект локализации Locale (см. ResourceActivator), то проверим это поочередной остановкой и стартом каждого из бандлов.


osgi> stop 1
stop bundle-resource

osgi> start 1
start bundle-resource

osgi> stop 2
stop bundle-locale

osgi> start 2
start bundle-locale
Locale : language = de, name = German

osgi> stop 1
stop bundle-resource

osgi> start 1
start bundle-resource

osgi> stop 2
stop bundle-locale

osgi> start 2
start bundle-locale
Locale : language = en, name = English

osgi> stop 1
stop bundle-resource

osgi> start 1
start bundle-resource

osgi> stop 2
stop bundle-locale

osgi> start 2
start bundle-locale
Locale : language = ru, name = Russian
 

Можно сказать, что все «задуманное сработало». Конечно же, при смене языка локализации в системе должен работать механизм «публикации/подписки», но это уже отдельная тема, рассмотренная здесь. Пример использования сервисных бандлов показывает, как можно создать и зарегистрировать сервис в контейнере OSGi, а также взаимодействие бандлов посредством сервисов.

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

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

  Рейтинг@Mail.ru