Введение в 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.