410013796724260
• Webmoney
R335386147728
Z369087728698
Пример OSGi сервиса сообщенийРассмотрим пример OSGi сервиса печати сообщений в консоль. Для этого будем использовать два бандла. Один бандл зарегистрируется в контейнере OSGi в качестве сервиса. Второй bundle будет вызывать сервис для «вывода» сообщений в консоль :
Описание и технологию создания bundle не рассматриваем. Желающие могут познакомиться как со спецификацией OSGi, так и со структурой бандлов здесь. Терминология «bundle» заимствована из спецификации OSGi. На следующем рисунке представлена схема возможных состояний OSGI bundle. Если с бандлом нет проблем, то после инсталляции (Install) он переходит в состяние Resolved. После этого бандл может быть стартован (start) и остановлен (stop). Проверим это при тестировании бандлов. Проблемы у бандла могут возникнуть, если в OSGi контейнере/фреймворке отсутствуют необходимые для его функционирования пакеты (package). В этом случае бандл остается в состоянии «Installed», и все попытки стартовать такой bundle сопровождаются вызовом исключений (Exception). Для проверки функционирования наших бандлов будет использоваться OSGi контейнер Equinox от Eclipse Foundation. Данный фреймворк позволяет проверить наличие необходимых для функционирования бандлов пакетов. Структуры проектов создания OSGi бандловНа следующем скриншоте представлена структура проектов OSGi. Для разработки OSGi бандлов используется фреймворк Maven. Bundle сервис service-printer включает :
Bundle клиент service-client включает :
OSGi сервис service-printerЛистинг интерфейса IPrinter.javaИнтерфейс IPrinter.java включает только один метод print, которому в качестве параметра необходимо передать текст сообщения. package osgi.service.printer; public interface IPrinter { public void print(String message); } Листинг имплементации PrinterImpl.javaИмплементация интерфейса PrinterImpl.java реализует метод print, в котором выводит текст сообщения в консоль. При выводе сообщения используется класс форматирования даты DateFormat пакета java.text. package osgi.service.printer; import java.util.Date; import java.text.DateFormat; public class PrinterImpl implements IPrinter { public void print(String message) { DateFormat dtf = DateFormat.getTimeInstance(DateFormat.LONG); String time = dtf.format(new Date()); System.out.println(time + " : " + message); } } Листинг активатора бандла PrinterActivator.javaАктиватор бандла реализует интерфейс BundleActivator, включающий методы start и stop. В методе start в контексте фреймворка регистрируется сервис serviceRegistration. В методе stop регистрация сервиса из фреймворка удаляется (unregister). package osgi.service.printer; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleActivator; import org.osgi.framework.ServiceRegistration; public class PrinterActivator implements BundleActivator { private ServiceRegistration<IPrinter> serviceRegistration; //--------------------------------------------------------------- @Override public void start(BundleContext context) throws Exception { serviceRegistration = (ServiceRegistration<IPrinter>) context.registerService (IPrinter.class.getName(), new PrinterImpl(), null); System.out.println("start : service-printer"); } //--------------------------------------------------------------- @Override public void stop(BundleContext context) throws Exception { serviceRegistration.unregister(); System.out.println("stop : service-printer"); } //--------------------------------------------------------------- } Листинг pom.xmlФайл описания сборки бандла pom.xml включает GAV параметры и полное наименование артефакта. В качестве зависимости используется org.osgi.core. <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>osgi.service.printer</groupId> <artifactId>service-printer</artifactId> <packaging>bundle</packaging> <version>1.0.0</version> <name>Service printer</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.core</artifactId> <version>6.0.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> </plugin> <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}.service-hello </Bundle-SymbolicName> <Bundle-Name> ${project.name} </Bundle-Name> <Bundle-Version> ${project.version} </Bundle-Version> <Bundle-Activator> osgi.service.printer.PrinterActivator </Bundle-Activator> <Import-Package> org.osgi.framework;version="1.6.0" </Import-Package> </instructions> </configuration> </plugin> </plugins> </build> </project> Особый интерес представляет секция Import-Package, в которой указывается зависимость от фреймворка org.osgi.framework версии "1.6.0", который будет использован для тестирования. Тестирование сервиса service-printerДля тестирования бандлов используется фреймворк Equinox (org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar) от Eclipse Foundation, который входит в архив примеров и размещается в отдельной поддиректории osgi.container. Команды фреймворка Equinox для просмотра и управления состояниями бандлов представлены здесь. В файле конфигурации configuration/config.ini определяется список бандлов. Внесем в него следующую строку : osgi.bundles=bundles/service-printer-1.0.0.jar@startВ результате фреймворк инсталлирует и сразу же стартует бандл «service-printer». Информацию фреймворк выводит в консоль в следующем виде : D:\osgi.container>java -jar org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar -console osgi> start : service-printer osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.7.1.R37x_v20110808-1106 1 ACTIVE osgi.service.printer.service-hello_1.0.0 osgi> services (objectClass=*Printer) {osgi.service.printer.IPrinter}={service.id=29} Registered by bundle: osgi.service.printer.service-hello_1.0.0 [1] No bundles using service. osgi> stop 1 stop : service-printer osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.7.1.R37x_v20110808-1106 1 RESOLVED osgi.service.printer.service-hello_1.0.0 По команде ss выводим список бандлов, включающий их идентификаторы и состояния. Командой services с соответствующей маской (objectClass=*Printer) выводим информацию о сервисе. После этого останавливаем бандл service-printer и просматриваем состояния бандлов. Пакеты фреймворкаДля просмотра используемых фрейморком пакетов необходимо выполнить команду packages, в результате чего в консоль будет выведен список всех пакетов. Обратите внимание, что после фреймворка org.osgi.framework (с версией "1.6.0") выводится используемый его бандл «service-hello» с атрибутом imports. osgi> packages ... org.osgi.framework; version="1.6.0"<org.eclipse.osgi_3.7.1.R37x_v20110808-1106 [0]> osgi.service.printer.service-hello_1.0.0 [1] imports ... osgi.service.printer; version="1.0.0"<osgi.service.printer.service-hello_1.0.0 [1]> OSGi bundle service-clientЛистинг класса RunnablePrinter.javaКласс RunnablePrinter.java реализует интерфейс Runnable, переопределяя методы start, stop и run. Метод setPrinterService в качестве параметра получает ссылку на сервис IPrinter. В методе run каждые 3 секунды вызывается метод print сервиса. Количество вызовов не превышает 5, после чего отправка сообщений прекращается. package osgi.service.client; import osgi.service.printer.IPrinter; public class RunnablePrinter implements Runnable { private final int TWO_SECS = 3000; private static int counter = 0; private boolean stop; private IPrinter service; void setPrinterService(IPrinter service) { this.service = service; } void start() { stop = false; new Thread(this).start(); } void stop() { stop = true; } public void run() { while (!stop) { service.print("hello [" + String.valueOf(++counter) + "] ..."); try { Thread.sleep(TWO_SECS); if (counter == 5) { counter = 0; stop = true; } } catch (InterruptedException e) { stop = true; } } } } Листинг активатора ClientActivator.javaАктиватор клиента ClientActivator.java реализует два интерфейса (BundleActivator, ServiceListener) переопределяя методы start, stop и serviceChanged. В первых двух методах выполняется подключение к сервису при старте бандла и отключение от сервиса при его останове. Метод serviceChanged фреймворк вызывает, когда регистрируется или останавливается соответствующий сервис. Если по каким-либо причинам не удается получить ссылку на сервис при старте, т.е. если сервис еще не зарегистрирован во фреймворке, то подключение будет выполнено сразу же после его регистрации уже при стартованном service-client. За это отвечает метод serviceChanged интерфейса ServiceListener. Сервис также может быть остановлен прежде чем будет остановлен бандл service-client. Тогда фреймворк также вызовет метод serviceChanged с соответствующим параметром ServiceEvent : serviceEvent.getType() == ServiceEvent.UNREGISTERING. package osgi.service.client; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleActivator; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; import osgi.service.printer.IPrinter; public class ClientActivator implements BundleActivator, ServiceListener { private RunnablePrinter runnablePrinter = null; private BundleContext bundleContext = null; private ServiceReference<IPrinter> serviceReference = null; //--------------------------------------------------------------- @Override public void start(BundleContext context) throws Exception { runnablePrinter = new RunnablePrinter(); this.bundleContext = context; serviceReference = (ServiceReference<IPrinter>) context.getServiceReference(IPrinter.class.getName()); if (serviceReference != null) { IPrinter printer = (IPrinter) context.getService(serviceReference); if (printer != null) { runnablePrinter.setPrinterService(printer); runnablePrinter.start(); } } context.addServiceListener(this, "(objectClass=" + IPrinter.class.getName() + ")"); System.out.println("start : service-client"); } //--------------------------------------------------------------- @Override public void stop(BundleContext context) throws Exception { if (serviceReference != null) context.ungetService(serviceReference); context.removeServiceListener(this); System.out.println("stop : service-client"); } //--------------------------------------------------------------- @Override public void serviceChanged(ServiceEvent serviceEvent) { switch (serviceEvent.getType()) { case ServiceEvent.UNREGISTERING : runnablePrinter.stop(); bundleContext.ungetService( serviceEvent.getServiceReference()); break; case ServiceEvent.REGISTERED : IPrinter printer = (IPrinter) bundleContext.getService( serviceEvent.getServiceReference()); if (printer != null) { runnablePrinter.setPrinterService(printer); runnablePrinter.start(); } break; } } //--------------------------------------------------------------- } Листинг pom.xmlЛистинг pom.xml клиента ни чем, кроме параметров GAV, не отличается от соответствующего файла сервиса, и его содержимое не приводится, чтобы не загромождать страницу дублируемой информацией. Желающие могут скачать рассматриваемые примеры, включающие также и файлы pom.xml. Тестирование сервиса взаимодействия бандловВнесем в файл конфигурации configuration/config.ini следующие строки : osgi.bundles=bundles/service-printer-1.0.0.jar@start, \osgi.bundles=bundles/service-client-1.0.0.jar@start Теперь фреймворк стартует оба бандла, и service-client сразу же начинает отправлять сообщения. Если просмотреть сервис IPrinter, то увидим, что он используется бандлом service-client. На следующем шаге останавливаем сервис и перестартуем клиента. Теперь клиент не может отправить сообщения и находится в режиме ожидания. Как только сервис стартует, отправка сообщений возобновится. D:\osgi.container>java -jar org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar -console osgi> start : service-printer start : service-client 14:26:24 MSK : hello [1] ... 14:26:27 MSK : hello [2] ... 14:26:30 MSK : hello [3] ... 14:26:33 MSK : hello [4] ... 14:26:37 MSK : hello [5] ... osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.7.1.R37x_v20110808-1106 1 ACTIVE osgi.service.printer.service-hello_1.0.0 2 ACTIVE osgi.service.client.service-client_1.0.0 osgi> services (objectClass=*IPrinter) {osgi.service.printer.IPrinter}={service.id=32} Registered by bundle: osgi.service.printer.service-hello_1.0.0 [1] Bundles using service: osgi.service.client.service-client_1.0.0 [2] osgi> stop 1 stop : service-printer osgi> stop 2 stop : service-client osgi> start 2 start : service-client osgi> start 1 start : service-printer osgi> 14:27:34 MSK : hello [1] ... 14:27:37 MSK : hello [2] ... 14:27:40 MSK : hello [3] ... 14:27:43 MSK : hello [4] ... 14:27:46 MSK : hello [5] ... osgi> stop 1 stop : service-printer osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.7.1.R37x_v20110808-1106 1 RESOLVED osgi.service.printer.service-hello_1.0.0 2 ACTIVE osgi.service.client.service-client_1.0.0 После получения всех сообщений останавливаем сервис и проверяем его состояние. Оно должно быть «RESOLVED». Пакеты фреймворкаПри просмотре пакетов обратите внимание на использование фреймворка org.osgi.framework бандлами service-hello и service-client, включающими атрибут imports, а также использование сервиса service-hello бандлом service-client. osgi> packages ... org.osgi.framework; version="1.6.0"<org.eclipse.osgi_3.7.1.R37x_v20110808-1106 [0]> osgi.service.printer.service-hello_1.0.0 [1] imports osgi.service.client.service-client_1.0.0 [2] imports ... osgi.service.printer; version="1.0.0"<osgi.service.printer.service-hello_1.0.0 [1]> osgi.service.client.service-client_1.0.0 [2] imports osgi.service.client; version="1.0.0"<osgi.service.client.service-client_1.0.0 [2]> Скачать исходный кодИсходные коды рассмотренных примеров в виде проектов Eclipse можно скачать здесь (1.22 Мб). Архив проекта дополнительно содержит директорию osgi.container, включающую фреймворк org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar для создания OSGi контейнера Equinox и тестирования бандлов. Примеры создания бандлов и организации взаимодействия представлены в разделе описания многомодульного OSGi-приложения на платформе JaBricks. |