Введение в OSGi

Введение в OSGi

1. Вступление

Некоторые критически важные и промежуточные приложения Java имеют жесткие технологические требования.

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

ПлатформыOSGi представляют собой жизнеспособное решение для поддержки такого рода требований.

The Open Service Gateway Initiative is a specification defining a Java-based component system. В настоящее время им управляетOSGi Alliance, а его первая версия датируется 1999 годом.

С тех пор он оказался отличным стандартом для компонентных систем и широко используется в настоящее время. Например,Eclipse IDE - это приложение на основеOSGi.

В этой статье мы рассмотрим некоторые основные особенностиOSGi, используя реализацию, предоставленнуюApache.

2. Основы OSGi

В OSGi отдельный компонент называется связкой.

По логике,a bundle is a piece of functionality that has an independent lifecycle – означает, что его можно запускать, останавливать и удалять независимо.

Технически пакет - это просто файл jar с файломMANIFEST.MF, содержащий некоторые заголовки, специфичные для OSGi.

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

Из-за этого пакет должен явно объявлять, к каким пакетам он должен иметь доступ, и платформаOSGi запустит его, только если зависимости доступны в самом пакете или в других пакетах, уже установленных на платформе.

3. Получение инструментов

Мы начнем наше путешествие вOSGi, загрузив последнюю версиюApache Karaf изthis link. Apache Karaf - это платформа, на которой выполняются приложения на основеOSGi-; он основан на реализацииApache спецификацииOSGi под названиемApache Felix.

Karaf предлагает некоторые удобные функции поверхFelix, которые помогут нам познакомиться сOSGi, например, интерфейс командной строки, который позволит нам взаимодействовать с платформой.

Чтобы установитьKaraf, вы можете следовать инструкциям по установке изofficial documentation.

4. Точка входа в комплект

Чтобы выполнить приложение в среде OSGi, мы должны упаковать его как пакетOSGi и определить точку входа приложения, а это не обычный методpublic static void main(String[] args).

Итак, давайте начнем с создания приложения Hello World на основеOSGi-.

Начнем настраивать простую зависимость от ядраOSGi API:


    org.osgi
    org.osgi.core
    6.0.0
    provided

Зависимость объявлена ​​какprovided, потому что она будет доступна во время выполненияOSGi, и не нужно ее встраивать в пакет.

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

public class HelloWorld implements BundleActivator {
    public void start(BundleContext ctx) {
        System.out.println("Hello world.");
    }
    public void stop(BundleContext bundleContext) {
        System.out.println("Goodbye world.");
    }
}

BundleActivator - это интерфейс, предоставляемыйOSGi, который должен быть реализован классами, которые являются точками входа для пакета.

Методstart() вызывается платформойOSGi при запуске пакета, содержащего этот класс. С другой стороны,stop() вызывается непосредственно перед остановкой пакета.

Напомним, что каждый пакет может содержать не более одногоBundleActivator. ОбъектBundleContext, предоставленный обоим методам, позволяет взаимодействовать со средой выполненияOSGi. Мы скоро вернемся к этому.

5. Создание пакета

Давайте изменимpom.xml и сделаем его настоящим пакетом OSGi.

Прежде всего, мы должны явно заявить, что мы собираемся создавать бандл, а не банку:

bundle

Затем мы используемmaven-bundle-plugin, любезно предоставленное сообществомApache Felix, чтобы упаковать классHelloWorld как пакетOSGi:


    org.apache.felix
    maven-bundle-plugin
    3.3.0
    true
    
        
            
                ${pom.groupId}.${pom.artifactId}
            
            ${pom.name}
            ${pom.version}
            
                com.example.osgi.sample.activator.HelloWorld
            
            
                com.example.osgi.sample.activator
            
        
    

В разделе инструкций мы указываем значения заголовковOSGi, которые мы хотим включить в файл МАНИФЕСТА пакета.

Bundle-Activator - это полное имя реализацииBundleActivator, которое будет использоваться для запуска и остановки пакета, и оно относится к классу, который мы только что написали.

Private-Package не является заголовком OSGi, но он используется для указания плагину включить пакет в пакет, но не делать его доступным для других. Теперь мы можем собрать пакет с помощью обычной командыmvn clean install.

6. Установка и запуск пакета

Давайте запустимKaraf, выполнив команду:

/bin/karaf start

где<KARAF_HOME> - это папка, в которую установленKaraf. Когда появится приглашение консолиKaraf, мы можем выполнить следующую команду для установки пакета:

> bundle:install mvn:com.example/osgi-intro-sample-activator/1.0-SNAPSHOT
Bundle ID: 63

Это указывает Карафу загрузить пакет из локального хранилища Maven.

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

> bundle:start 63
Hello World

«Hello World» появляется сразу после запуска пакета. Теперь мы можем остановить и удалить пакет с помощью:

> bundle:stop 63
> bundle:uninstall 63

«Прощай, мир» появляется на консоли в соответствии с кодом в методеstop().

7. Служба OSGi

Давайте продолжим писать простой сервисOSGi, интерфейс, который предоставляет метод для приветствия людей:

package com.example.osgi.sample.service.definition;
public interface Greeter {
    public String sayHiTo(String name);
}

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

package com.example.osgi.sample.service.implementation;
public class GreeterImpl implements Greeter, BundleActivator {

    private ServiceReference reference;
    private ServiceRegistration registration;

    @Override
    public String sayHiTo(String name) {
        return "Hello " + name;
    }

    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Registering service.");
        registration = context.registerService(
          Greeter.class,
          new GreeterImpl(),
          new Hashtable());
        reference = registration
          .getReference();
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Unregistering service.");
        registration.unregister();
    }
}

Мы используемBundleContext как средство запроса платформыOSGi для регистрации нового экземпляра службы.

Мы также должны предоставить тип сервиса и карту возможных параметров конфигурации, которые не нужны в нашем простом сценарии. Теперь приступим к настройкеmaven-bundle-plugin:


    org.apache.felix
    maven-bundle-plugin
    true
    
        
            
                ${project.groupId}.${project.artifactId}
            
            
                ${project.artifactId}
            
            
                ${project.version}
            
            
                com.example.osgi.sample.service.implementation.GreeterImpl
            
            
                com.example.osgi.sample.service.implementation
            
            
                com.example.osgi.sample.service.definition
            
        
    

Стоит отметить, что на этот раз был экспортирован только пакетcom.example.osgi.sample.service.definition через заголовокExport-Package.

Благодаря этомуOSGi позволит другим пакетам вызывать только методы, указанные в интерфейсе службы. Пакетcom.example.osgi.sample.service.implementation помечен как частный, поэтому ни один другой пакет не сможет напрямую получить доступ к членам реализации.

8. Клиент OSGi

Теперь напишем клиенту. Он просто ищет сервис при запуске и вызывает его:

public class Client implements BundleActivator, ServiceListener {
}

Давайте реализуем методBundleActivator start():

private BundleContext ctx;
private ServiceReference serviceReference;

public void start(BundleContext ctx) {
    this.ctx = ctx;
    try {
        ctx.addServiceListener(
          this, "(objectclass=" + Greeter.class.getName() + ")");
    } catch (InvalidSyntaxException ise) {
        ise.printStackTrace();
    }
}

МетодaddServiceListener() позволяет клиенту запрашивать у платформы отправку уведомлений о службе, соответствующей предоставленному выражению.

В выражении используется синтаксис, аналогичный синтаксису LDAP, и в нашем случае мы запрашиваем уведомления о службеGreeter.

Перейдем к методу обратного вызова:

public void serviceChanged(ServiceEvent serviceEvent) {
    int type = serviceEvent.getType();
    switch (type){
        case(ServiceEvent.REGISTERED):
            System.out.println("Notification of service registered.");
            serviceReference = serviceEvent
              .getServiceReference();
            Greeter service = (Greeter)(ctx.getService(serviceReference));
            System.out.println( service.sayHiTo("John") );
            break;
        case(ServiceEvent.UNREGISTERING):
            System.out.println("Notification of service unregistered.");
            ctx.ungetService(serviceEvent.getServiceReference());
            break;
        default:
            break;
    }
}

Когда происходит какое-либо изменение, связанное с сервисомGreeter, метод уведомляется.

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

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

Теперь нам просто нужно написать методstop():

public void stop(BundleContext bundleContext) {
    if(serviceReference != null) {
        ctx.ungetService(serviceReference);
    }
}

Здесь снова мы отключаем службу, чтобы охватить случай, в котором клиент останавливается до остановки службы. Давайте в заключение посмотрим на зависимости вpom.xml:


    com.example
    osgi-intro-sample-service
    1.0-SNAPSHOT
    provided


    org.osgi
    org.osgi.core
    6.0.0

9. Клиент и Сервис

Теперь давайте установим пакеты клиента и службы в Karaf, выполнив следующие действия:

> install mvn:com.example/osgi-intro-sample-service/1.0-SNAPSHOT
Bundle ID: 64
> install mvn:com.example/osgi-intro-sample-client/1.0-SNAPSHOT
Bundle ID: 65

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

Теперь запустим клиентский пакет:

> start 65

Следовательно, ничего не происходит, потому что клиент активен и ждет службы, с которой мы можем начать:

> start 64
Registering service.
Service registered.
Hello John

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

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

10. Заключение

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

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

Код этого поста можно найтиover on GitHub.