Пример использования OSGi
Немного о технологии OSGi:
Технология
OSGi представляет собой набор спецификаций,
которые определяют модель для динамического
связывания компонентов на языке Java
между собой.
Разработка
ведется под OSGi Alliance (более подробная
информация доступна
тут
Спецификации
позволяют разбивать приложение на
компоненты (сервисы),
которые легко использовать
повторно и которые связываются
динамически. Сервисом может
стать любой Java объект,
зарегистрированный в OSGi. Для для классов, которые обращаются к нему предоставляется лишь интерфейс (реализация скрывается).
На странице описаны основные достоинства OSGi.
Основные компоненты OSGi
Bundle (каталог или JAR архив) содержит java-классы и другие ресурсы, которые вместе могут реализовывать некие функции, а также предоставлять сервисы и пакеты другим bundle.
В bundle входят следующие компоненты:
- Классы и ресурсы в которых реализована функциональность
- Класс активатор (может отсутствовать)
- Файл META-INF/MANIFEST.MF – в нем определяются параметры bundle.
Контейнер - приложение, которое предназначено для запуска и управления bundles. В настоящий момент, реализованы следующий OSGi контейнеры: Virgo, Equinox, ApacheFelix, Knopflerfish
Жизненый цикл bundle
Bundle может находится в одном из состояний, описание которых приведено ниже:
- INSTALLED - успешно установлен в контейнер
- RESOLVED - разрешены все зависимости, доступны все Java-классы и bundle, от которых он зависит. Из этого состояния возможен запуск bundle
- STARTING – запуск, метод BundleActivator.start() выполняется и пока не вернул управление
- ACTIVE - bundle успешно запустился. Метод BundleActivator.start() вернул управление
- STOPPING – процесс остановки, метод BundleActivator.stop() вызван, но пока не вернул управление.
- UNINSTALLED – удален из контейнера, соответственно он не может переходить в другие состояния. Жизненный цикл бандла завершен.
Пример использования OSGi: TimeServer
TimeServer демонстрирует следующие случаи использования OSGi:
- Публикация библиотеки как OSGi контейнера
- Регистрация OSGi сервиса (использования BundleActivator)
- Получение ссылки на OSGi сервис (используя ServiceListiner)
- Работа с OSGi консолью
Для демонстрации
возможностей OSGi было разработано
приложение TimeServer, которое показывает
текущее время в аэропорту по
его коду.
Приложение разбито на отдельные bundle, для того,
чтобы можно было вести независимую
разработку и показать возможности независимого изменение компонентов системы.
Компоненты (bundle) примера:
- AirportInfoService - библиотека для определения информации об аэропорте по его коду
- TimeServiceInterface - bundle, в котором находятся интерфейсы
- TimeService - сервис для определения по коду аэропорта времени в нем
- WSTimeServer- сервер, принимающий запросы по WebServices и возвращающий время в городе.
Между компонентами существуют следующие зависимости:
TimeService - AirportInfoService: TimeService вызывает методы класса AirportInfoProvider, который находится в AirportInfoServis, при этом происходит вызов статического метода. Ссылка на класса получается через простой импорт.
WSTimeServer - TimeService: WSTimeServer является RemoteFacade для TimeService, предоставляя возможность вызывать методы через WebServices.
TimeService регистрирует OSGi сервис используя интерфейс TimeService (берется из bundle TimiServiceInterface), в свою очередь WSTimeServer получает на него ссылку через BundleContext.
AirportInfoService
В данном bundle
находятся классы для определения параметров аэропорта по его коду. Bundle не имеет активатора, классы доступны как простые Java классы. Ниже приведены листинги классов.
Airport
package lv.nixx.osgi.sample.countryservice;import java.util.TimeZone;
public class Airport {
private TimeZone timeZone;
private String code;
private String name;
private Airport(TimeZone timeZone, String code, String name) {
this.timeZone = timeZone;
this.code = code;
this.name = name;
}
static Airport create(TimeZone timeZone, String code, String name) {
return new Airport(timeZone, code, name);
}
public TimeZone getTimeZone() {
return timeZone;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}
AirportInfoProvider
package lv.nixx.osgi.sample.countryservice;
import java.util.*;
public class AirportInfoProvider {
static Map<String, Airport> ai
= new HashMap<String,Airport>();
static {
ai.put("RIX",
Airport.create(TimeZone.getTimeZone("GMT+3"),
"RIX", "Riga Airport"));
ai.put("KBP",
Airport.create(TimeZone.getTimeZone("GMT+3"),
"KBP", "Kyiv - Borispol Airport"));
ai.put("SVO",
Airport.create(TimeZone.getTimeZone("GMT+3"),
"SVO",
"Moscow Sheremetyevo Airport"));
ai.put("CDG",
Airport.create(TimeZone.getTimeZone("GMT+2"),
"CDG",
"Paris-Charles de Gaulle Airport"));
ai.put("LGW",
Airport.create(TimeZone.getTimeZone("GMT+0"),
"LGW",
"London Gatwick Airport"));
}
public static Airport getAirportByCode(String code)
throws Exception {
if (!ai.containsKey(code)) {
throw new Exception(
"Airport with code [" + code + "] not found");
}
return ai.get(code);
}
}
import java.util.*;
public class AirportInfoProvider {
static Map<String, Airport> ai
= new HashMap<String,Airport>();
static {
ai.put("RIX",
Airport.create(TimeZone.getTimeZone("GMT+3"),
"RIX", "Riga Airport"));
ai.put("KBP",
Airport.create(TimeZone.getTimeZone("GMT+3"),
"KBP", "Kyiv - Borispol Airport"));
ai.put("SVO",
Airport.create(TimeZone.getTimeZone("GMT+3"),
"SVO",
"Moscow Sheremetyevo Airport"));
ai.put("CDG",
Airport.create(TimeZone.getTimeZone("GMT+2"),
"CDG",
"Paris-Charles de Gaulle Airport"));
ai.put("LGW",
Airport.create(TimeZone.getTimeZone("GMT+0"),
"LGW",
"London Gatwick Airport"));
}
public static Airport getAirportByCode(String code)
throws Exception {
if (!ai.containsKey(code)) {
throw new Exception(
"Airport with code [" + code + "] not found");
}
return ai.get(code);
}
}
Содержимое
файла MANIFEST.MF показано ниже:
Manifest-Version: 1.0
Bundle-ManifestVersion:
2
Bundle-Name:
AirportInfoService
Bundle-SymbolicName:
AirportInfoService
Bundle-Version:
1.0.1.qualifier
Import-Package:
org.osgi.framework;version="1.3.0"
Export-Package:
lv.nixx.osgi.sample.countryservice
Данный bundle не
имеем активатора, соответственно, обращение
к классам возможно только
как к простым Java классам, а не как к OSGi
сервису. Другим bundle предоставляются классы из пакета lv.nixx.osgi.sample.countryservice.
TimeServiceInterface
В данный bundle вынесен интерфейс TimeService, это необходимо, чтобы отделить реализацию от интерфейса. В этом случае, будет возможно обновление TimeService bundle на "лету", при помощи update, не затрагивая WSTimeService.
TimeService
Код интерфейса TimeService показан ниже:
MANIFEST.MF
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TimeServiceInterface
Bundle-SymbolicName: TimeServiceInterface
Bundle-Version: 1.0.0.qualifier
Export-Package: lv.nixx.osgi.sample.serviceinterface
Другим bundle предоставляются классы из пакета: lv.nixx.osgi.sample.serviceinterface
package lv.nixx.osgi.sample.serviceinterface;
public interface TimeService {
public String getTimeByAirportCode(String code)
throws Exception;
throws Exception;
}
MANIFEST.MF
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TimeServiceInterface
Bundle-SymbolicName: TimeServiceInterface
Bundle-Version: 1.0.0.qualifier
Export-Package: lv.nixx.osgi.sample.serviceinterface
Другим bundle предоставляются классы из пакета: lv.nixx.osgi.sample.serviceinterface
TimeService
Представляет
собой сервис, который возвращает
информацию о времени в аэропорту по его
коду. Для того, чтобы сервис, был доступен
для других bundle как OSGi сервис необходимо в нем необходимо
реализовать класс активатор, код которого
показан ниже:
package lv.nixx.osgi.sample;TimeServiceActivator
import java.util.*;
import lv.nixx.osgi.sample.service.*;
import lv.nixx.osgi.sample.serviceinterface.TimeService;
import org.osgi.framework.*;
public class TimeServiceActivator implements BundleActivator {
public ServiceRegistration serviceRegistration;
@Override
public void start(BundleContext bundleContext)
throws Exception {
Dictionary<String, String> properties
= new Hashtable<String, String>();
properties.put("description",
"TimeService, provide service " +
"to get time in airport by code");
serviceRegistration = bundleContext.registerService(
TimeService.class.getName(),
new TimeServiceImpl(),
properties);
System.out.println("'TimeService' bundle started");
}
@Override
public void stop(BundleContext bundleContext)
throws Exception {
serviceRegistration.unregister();
System.out.println("'TimeService' bundle stoped");
}
}Реализуя BundleActivator у разработчика есть возможность управлять жизненным циклом bundle в OSGi контейнере (один bundle может содержать только один активатор), При этом, в одном bundle может быть зарегистрировано любое количество сервисов.
Для того, чтобы контейнер начал
воспринимать класс как активатор,
должны быть выполнены следующие условия:
- класс должен реализовывать интерфейс BundleActivator. У разработчика есть возможность определить действия, которые будут происходить при старте bundle (метод start) и при остановки bundle (метод stop).
- В файле MANIFEST.MF должен
быть прописан класс — активатор:
Bundle-Activator: lv.nixx.osgi.sample.TimeServiceActivator
TimeServiceImpl
Код класса TimeServiceImpl, в котором реализована функциональность по определению времени в аэропорту показан ниже.
import java.text.*;
import java.util.*;
import lv.nixx.osgi.sample.countryservice.*;
import lv.nixx.osgi.sample.serviceinterface.TimeService;
public class TimeServiceImpl implements TimeService {
public String getTimeByAirportCode(String code)
throws Exception {
Airport airport =
AirportInfoProvider.getAirportByCode(code);
DateFormat dateFormat =
new SimpleDateFormat("z yyyy.MM.dd HH.mm.sss");
dateFormat.setTimeZone(airport.getTimeZone());
String formatedDateTime = dateFormat.format(new Date());
return buildResponseString(airport, formatedDateTime);
}
private String buildResponseString(
Airport airport, String formatedDateTime) {
StringBuilder sb = new StringBuilder();
sb.append("time at airport, code:");
sb.append(airport.getCode());
sb.append("(");
sb.append(airport.getName());
sb.append(") [");
sb.append(formatedDateTime);
sb.append("]");
return sb.toString();
}
}
MANIFEST.MF
В файле, MANIFEST.MF определяются параметры bundle
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TimeService
Bundle-SymbolicName: TimeService
Bundle-Version: 1.0.1.qualifier
Bundle-Activator: lv.nixx.osgi.sample.TimeServiceActivator
Import-Package: lv.nixx.osgi.sample.countryservice;version="[0.0.0,2.0.0)",
org.osgi.framework;version="1.3.0", lv.nixx.osgi.sample.serviceinterface
Bundle-ManifestVersion: 2
Bundle-Name: TimeService
Bundle-SymbolicName: TimeService
Bundle-Version: 1.0.1.qualifier
Bundle-Activator: lv.nixx.osgi.sample.TimeServiceActivator
Import-Package: lv.nixx.osgi.sample.countryservice;version="[0.0.0,2.0.0)",
org.osgi.framework;version="1.3.0", lv.nixx.osgi.sample.serviceinterface
Расмотрим значения параметров:
- Import-Package: определяют пакеты, в которых находятся классы необходимые для работы, видим, что необходимы классы из пакета lv.nixx.osgi.sample.countryservice и lv.nixx.osgi.sample.serviceinterface. При этом, обращаем внимание, что нет явного указания на имя bundle где находится пакет, что позволяет использовать различные реализации.
- Bundle-Activator: при старте bundle запускается класс:
lv.nixx.osgi.sample.TimeServiceActivator
WSTimeServer
Данный bundle представляет собой RemoteFacade для TimeService, вынесение его в отдельный bundle понадобилось для того, чтобы можно было изменять cервис TimeService не затрагивая данного объекта.MANIFEST.MF
Рассмотрим MANIFEST.MF данного сервиса:Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: WSTimeServer
Bundle-SymbolicName: WSTimeServer
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: lv.nixx.osgi.sample.timeserver.WSTimeServerStarter
Import-Package: lv.nixx.osgi.sample.serviceinterface;version="[1.0.0,2.0.0)",
org.osgi.framework;version="1.3.0"
Данный bundle активируется при помощи класса WSTimeServerStarted, для его работы нужны классы из пакета lv.nixx.osgi.sample.serviceinterface, другим сервисам никакие классы не предоставляются.
WSTimeServerStarter
Ниже показан код класса активатора, при помощи которого сервис запускается как WebService и происходит получение ссылки на TimeService.package lv.nixx.osgi.sample.timeserver;
import java.util.*;
import lv.nixx.osgi.sample.serviceinterface.TimeService;
import lv.nixx.osgi.sample.timeserver.service.*;
import org.osgi.framework.*;
public class WSTimeServerStarter implements BundleActivator {
private ServiceRegistration registration;
private WSTimeServerImpl wsTimeServerImpl = null;
public void start(final BundleContext context)
throws Exception {
wsTimeServerImpl = new WSTimeServerImpl();
Dictionary<String, String> properties
= new Hashtable<String, String>();
String hostAddress = "http://localhost:9001/TimeServer";
properties.put("service.exported.interfaces", "*");
properties.put("service.exported.configs", "pojo");
properties.put("org.apache.cxf.ws.address", hostAddress);
TimeServiceListener listener = new TimeServiceListener();
String serviceFilter = "(objectclass=" +
TimeService.class.getName() +
")";
context.addServiceListener(listener, serviceFilter);
// possible situation when TimeService already had
// been registered, the listener above would not
// be called until the TimeService were restarted.
// We must manually fire this event in listener
fireEvent_REGISTERED(context, listener, serviceFilter);
registration =
context.registerService(WSTimeServer.class.getName(),
wsTimeServerImpl,
properties);
System.out.println("TimeServer registration success, " +
"ws.address [" + hostAddress + "]");
}
private void fireEvent_REGISTERED(BundleContext context,
ServiceListener serviceListiner, String filter)
throws InvalidSyntaxException {
ServiceReference[] lst
= context.getServiceReferences(null, filter);
for (int i = 0; lst != null && i < lst.length; i++) {
final ServiceReference sr = lst[i];
serviceListiner.serviceChanged(
new ServiceEvent(
ServiceEvent.REGISTERED, sr));
}
}
public void stop(BundleContext context) throws Exception {
registration.unregister();
wsTimeServerImpl = null;
System.out.println("WS TimeServer unregistered");
}
private class TimeServiceListener implements ServiceListener{
public void serviceChanged(ServiceEvent serviceEvent) {
ServiceReference sr = serviceEvent.getServiceReference();
final int eventType = serviceEvent.getType();
switch (eventType) {
case ServiceEvent.UNREGISTERING: {
System.out.println("Event 'UNREGISTERING'("
+ eventType + ") for service " +
"["+ sr+ "] is fired");
}
break;
case ServiceEvent.REGISTERED: {
System.out.println("Event 'REGISTERED' ("
+ eventType + ") for service " +
"["+ sr+ "] is fired");
Bundle bundle = sr.getBundle();
TimeService timeService =
(TimeService)bundle
.getBundleContext()
.getService(sr);
wsTimeServerImpl.setTimeService(timeService);
}
break;
default:
break;
}
}
};
}
В классе находится внутрений класс TimeServiceListiner (реализует интерфейс ServiceListiner), данный класс является слушателем для сервиса TimeService. При изменении состояния сервиса вызывается метод serviceChanged, при событии REGISTERED, происходит получение ссылки на сервис TimeService и ее передача экземпляру класса WSServiceImpl для вызова методов. Обратим внимание на то, что мы принудительно инициируем событие REGISTERED при помощи метода fireEvent_REGISTERED это необходимо для того, чтобы небыло зависимости от порядка запуска bundles. Существует вероятность того, что TimeService запустится прежде чем WSTimeService и в этом случае, событие REGISTERED не будет инициировано.
Использовании listiner обеспечивает возможность изменения TimeService "на лету", при изменении достаточно лишь вызвать update в консоли сервера, после чего, WSTimeService начнет работать с новой версией.
WSTimeService
Интерфейс WSTimeService необходим для работы сервера WebService, именно этот интерфейс предоставляется клиентам, код интерфейса показан ниже.
package lv.nixx.osgi.sample.timeserver.service;
public interface WSTimeServer {
public String getTime(String airportCode) throws Exception;
}
WSTimeServiceImpl
В классе WSTimeServiceImpl происходит реализация интрефейса WSTimeService. Реализация тривиальна, в методе getTime() происходит делигирование вызова в TimeService, код показан ниже.
package lv.nixx.osgi.sample.timeserver.service;
import lv.nixx.osgi.sample.serviceinterface.TimeService;
public class WSTimeServerImpl implements WSTimeServer {
private TimeService timeService = null;
public void setTimeService(TimeService timeService){
this.timeService = timeService;
}
@Override
public String getTime(String airportCode) throws Exception {
if (timeService == null) {
throw new IllegalStateException(
"TimeService not available in this moment");
}
return timeService.getTimeByAirportCode(airportCode);
}
}
Тестовый клиент (WS Client)
Для тестирования работы приложения был написан тестове клиенты, который вызывает WSTimeServer через webservices. Один тестовый клиент делает одиночный запрос на сервер, другой отправляет запросы на сервер в бесконечном цикле (между запросам поток засыпает на 500 мс).
Генерация Java кода необходимого для обращению к WebServer выполнена при помощи запуска класса org.apache.axis.wsdl.WSDL2Java со следующими параметрами:
-o ./generated -p lv.nixx.osgi.sample.ws http://localhost:9001/TimeServer?wsdl
В момент запуска, сервер должен быть запущен, поскольку WSDL берется с сервера.
GenericTimeServerTester
Общая фунциональность вынесена в базовый класс GenericTimeServerTester, код которого показан ниже.
package lv.nixx.osgi.sample.timeservice.client;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.log4j.Logger;
import lv.nixx.osgi.sample.ws.Exception;
import lv.nixx.osgi.sample.ws.WSTimeServerLocator;
import lv.nixx.osgi.sample.ws.WSTimeServerPortType;
public abstract class GenericTimeServerTester {
protected Logger logger = Logger.getLogger(this.getClass());
protected WSTimeServerPortType getReferenceWSServer(String host)
throws MalformedURLException, ServiceException {
WSTimeServerLocator proxy = new WSTimeServerLocator();
final URL timeServerURL = new URL(host);
final WSTimeServerPortType timeServerServicePort =
proxy.getWSTimeServerPort(timeServerURL);
return timeServerServicePort;
}
protected void getTimeInRandomAirport(
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.log4j.Logger;
import lv.nixx.osgi.sample.ws.Exception;
import lv.nixx.osgi.sample.ws.WSTimeServerLocator;
import lv.nixx.osgi.sample.ws.WSTimeServerPortType;
public abstract class GenericTimeServerTester {
protected Logger logger = Logger.getLogger(this.getClass());
protected WSTimeServerPortType getReferenceWSServer(String host)
throws MalformedURLException, ServiceException {
WSTimeServerLocator proxy = new WSTimeServerLocator();
final URL timeServerURL = new URL(host);
final WSTimeServerPortType timeServerServicePort =
proxy.getWSTimeServerPort(timeServerURL);
return timeServerServicePort;
}
protected void getTimeInRandomAirport(
WSTimeServerPortType timeServerServicePort)
throws RemoteException {
String[] codeArray =
new String[]{"RIX","KBP","SVO","CDG","LGW"};
final int randomPosition =
(int)(Math.random() * codeArray.length);
logger.debug("TimeServer response [" +
timeServerServicePort.getTime(
codeArray[randomPosition]) +
"]");
}
protected abstract void executeTest() throws Exception;
}
throws RemoteException {
String[] codeArray =
new String[]{"RIX","KBP","SVO","CDG","LGW"};
final int randomPosition =
(int)(Math.random() * codeArray.length);
logger.debug("TimeServer response [" +
timeServerServicePort.getTime(
codeArray[randomPosition]) +
"]");
}
protected abstract void executeTest() throws Exception;
}
SingleRequestCaller
Код класса для единичного обращения к серверу показан ниже:
package lv.nixx.osgi.sample.timeservice.client;
import java.net.MalformedURLException;
import javax.xml.rpc.ServiceException;
import lv.nixx.osgi.sample.ws.*;
import lv.nixx.osgi.sample.ws.Exception;
public class SingleRequestCaller
extends GenericTimeServerTester {
public static void main(String[] args) throws Exception {
SingleRequestCaller tester = new SingleRequestCaller();
tester.executeTest();
}
@Override
protected void executeTest() throws Exception {
try {
final WSTimeServerPortType timeServerServicePort =
getReferenceWSServer(
"http://localhost:9001/TimeServer");
try {
this.getTimeInRandomAirport(timeServerServicePort);
} catch (java.lang.Throwable ex) {
logger.error(ex, ex);
}
} catch (MalformedURLException e) {
final Exception exception = new Exception();
exception.initCause(e);
throw exception;
} catch (ServiceException e) {
final Exception exception = new Exception();
exception.initCause(e);
throw exception;
}
}
}
import java.net.MalformedURLException;
import javax.xml.rpc.ServiceException;
import lv.nixx.osgi.sample.ws.*;
import lv.nixx.osgi.sample.ws.Exception;
public class SingleRequestCaller
extends GenericTimeServerTester {
public static void main(String[] args) throws Exception {
SingleRequestCaller tester = new SingleRequestCaller();
tester.executeTest();
}
@Override
protected void executeTest() throws Exception {
try {
final WSTimeServerPortType timeServerServicePort =
getReferenceWSServer(
"http://localhost:9001/TimeServer");
try {
this.getTimeInRandomAirport(timeServerServicePort);
} catch (java.lang.Throwable ex) {
logger.error(ex, ex);
}
} catch (MalformedURLException e) {
final Exception exception = new Exception();
exception.initCause(e);
throw exception;
} catch (ServiceException e) {
final Exception exception = new Exception();
exception.initCause(e);
throw exception;
}
}
}
InfiniteServerCaller
Для обращения к серверу в бесконечном цикле разработан класс InfiniteServerCaller код которого показан ниже.
package lv.nixx.osgi.sample.timeservice.client;
import java.net.MalformedURLException;
import javax.xml.rpc.ServiceException;
import lv.nixx.osgi.sample.ws.*;
import lv.nixx.osgi.sample.ws.Exception;
public class InfiniteServerCaller
extends GenericTimeServerTester {
public static void main(String[] args) throws Exception {
InfiniteServerCaller tester = new InfiniteServerCaller();
tester.executeTest();
}
@Override
protected void executeTest() throws Exception {
try {
WSTimeServerPortType timeServerServicePort =
getReferenceWSServer(
"http://localhost:9001/TimeServer");
"http://localhost:9001/TimeServer");
Object lock = new Object();
while (true) {
try {
getTimeInRandomAirport(
timeServerServicePort);
getTimeInRandomAirport(
timeServerServicePort);
synchronized (lock) {
try {
lock.wait(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (java.lang.Throwable ex) {
logger.error(ex, ex);
}
}
} catch (MalformedURLException e) {
final Exception exception = new Exception();
exception.initCause(e);
throw exception;
} catch (ServiceException e) {
final Exception exception = new Exception();
exception.initCause(e);
throw exception;
}
}
}
Основные консольные команды для работы с контейнером (Equinox)
Отображение статусов Bundle
ss - отображение (краткое) статуса установленных bundles
Пример вывода команды:
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
3 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
34 ACTIVE AirportInfoService_1.0.4.qualifier
42 ACTIVE TimeService_1.0.2.qualifier
43 ACTIVE WSTimeServer_1.0.0.qualifier
headers - заголовки конкретного bundle
Команда выводить заголовки конкретного bundle (по id), ниже показан вывод для TimeService bundle.
osgi> headers 42
Bundle headers:
Bundle-Activator = lv.nixx.osgi.sample.TimeServiceActivator
Bundle-ManifestVersion = 2
Bundle-Name = TimeService
Bundle-SymbolicName = TimeService
Bundle-Version = 1.0.2.qualifier
Export-Package = lv.nixx.osgi.sample.serviceinterface;version="1.0.1"
Import-Package = lv.nixx.osgi.sample.countryservice;version="[0.0.0,2.0.0)",org.osgi.framework;version="1.3.0"
Manifest-Version = 1.0
services - отображение зарегистрированных сервисов
Команда отображает сервисы зарегистрированные bundle, в параметрах можно передать фильтр, в которым указать критерии сервисов, которые выводить.Пример выполнения команды services (objectClass=lv.nixx.*)
osgi> services (objectClass=lv.nixx.*)
{lv.nixx.osgi.sample.serviceinterface.TimeService}=
{description=TimeService, provide service to get time in airport by code, service.id=28}
Registered by bundle: TimeService_1.0.2.qualifier [42]
Bundles using service:
WSTimeServer_1.0.0.qualifier [43]
{lv.nixx.osgi.sample.timeserver.service.WSTimeServer}={org.apache.cxf.ws.address=http://localhost:9001/TimeServer, service.exported.configs=pojo, service.exported.interfaces=*, service.id=42}
Registered by bundle: WSTimeServer_1.0.0.qualifier [43]
Bundles using service:
cxf-dosgi-ri-singlebundle-distribution_1.3.0 [3]
Управление жизненым циклом bundle
install - установка bundle в контейнере (параметром команды является URL).
uninstall - удаление bundle из контейнера
start - запуск bundle
stop - остановка bundle
refresh - обновление пакетов в bundle
update - обновление bundle
Тестовые сценарии работы приложения
Запуск приложения вызов сервера
Тестовый сценарий:- запускаем контейнер с установленными bundle
- проверяем состояние bundle (команда ss)
- выполняем запрос от клиента
После запуска приложения выполняем команду ss, получаем список установленных bundle с их состоянием
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
48 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
49 ACTIVE WSTimeServer_1.0.0.qualifier
51 ACTIVE TimeService_1.0.3.qualifier
52 ACTIVE TimeServiceInterface_1.0.0.qualifier
53 ACTIVE AirportInfoService_1.0.4.qualifier
Лог на стороне клиента:
27/06/12 15:14:08,633 [main] TimeServer response [time at airport, code:SVO(Moscow Sheremetyevo Airport) [GMT+03:00 2012.06.27 15.14.008]]
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
48 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
49 ACTIVE WSTimeServer_1.0.0.qualifier
51 ACTIVE TimeService_1.0.3.qualifier
52 ACTIVE TimeServiceInterface_1.0.0.qualifier
53 ACTIVE AirportInfoService_1.0.4.qualifier
Лог на стороне клиента:
27/06/12 15:14:08,633 [main] TimeServer response [time at airport, code:SVO(Moscow Sheremetyevo Airport) [GMT+03:00 2012.06.27 15.14.008]]
Обновление TimeService bundle
Тестовый сценарий
- запускаем контейнер с установленными bundle
- проверяем состояние bundle (команда ss)
- выполняем запрос от клиента
- изменим строку возвращаемую TimeServiceImpl
- выполняем обновление TimeService bundle (команда update)
- выполняем запрос от клиента
- проверяем состояние bundle (команда ss)
Выполнение тестового сценария
- запускаем контейнер с установленными bundle
- проверяем состояние bundle (команда ss)
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
48 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
49 ACTIVE WSTimeServer_1.0.0.qualifier
51 ACTIVE TimeService_1.0.3.qualifier
52 ACTIVE TimeServiceInterface_1.0.0.qualifier
53 ACTIVE AirportInfoService_1.0.4.qualifier
Обращаем внимаение на версию TimeService - 1.0.3 - выполняем запрос от клиента
27/06/12 15:26:59,297 [main] TimeServer response [time at airport, code:KBP(Kyiv - Borispol Airport) [GMT+03:00 2012.06.27 15.26.059]] - изменим строку возвращаемую TimeServiceImpl
Теперь должна возвращатся строка начинающееся с "NEW time at airport..."
Также изменяем в файле MANIFEST.MF изменяем номер версии bundle c 1.0.3 на 1.0.4 (строка Bundle-Version). - выполняем обновление TimeService bundle (команда update)
osgi> update 51
Event 'UNREGISTERING'(4) for service [{lv.nixx.osgi.sample.serviceinterface.TimeService}={description=TimeService, provide service to get time in airport by code, service.id=42}] is fired
'TimeService' bundle stoped
Event 'REGISTERED' (1) for service [{lv.nixx.osgi.sample.serviceinterface.TimeService}={description=TimeService, provide service to get time in airport by code, service.id=43}] is fired
'TimeService' bundle startedПо логам видим, что листенер получил события UNREGISTERING и REGISTERED, по второму событию мы обновили ссылку на TimeService в сервисе WSTimeService - выполняем запрос от клиента
27/06/12 15:31:25,270 [main] TimeServer response [NEW time at airport, code:RIX(Riga Airport) [GMT+03:00 2012.06.27 15.31.025]]
По логу клиента видим, что сервер ответил новой строкой, т.е мы изменили поведения сервера и изменения стали доступны клиенту без перезапуска сервера. - проверяем состояние bundle (команда ss)
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
48 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
49 ACTIVE WSTimeServer_1.0.0.qualifier
51 ACTIVE TimeService_1.0.4.qualifier
52 ACTIVE TimeServiceInterface_1.0.0.qualifier
53 ACTIVE AirportInfoService_1.0.4.qualifierВидим, что изменился номер версии TimeService на 1.0.4.
Если после запуска контейнера запустить тест, который бесконечно отправляет запросы на сервер, то по его логам будет видно, обновление компонента TimeService не повлияет на его работу, т.е после изменнения возвращаемой строки клиент просто начнет получать новую строку. Ниже показан пример лога.
27/06/12 15:58:25,375 [main] TimeServer response [time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.58.025]]
27/06/12 15:58:25,884 [main] TimeServer response [time at airport, code:SVO(Moscow Sheremetyevo Airport) [GMT+03:00 2012.06.27 15.58.025]]
27/06/12 15:58:26,393 [main] TimeServer response [time at airport, code:CDG(Paris-Charles de Gaulle Airport) [GMT+02:00 2012.06.27 14.58.026]]
27/06/12 15:58:26,902 [main] TimeServer response [NEW time at airport, code:SVO(Moscow Sheremetyevo Airport) [GMT+03:00 2012.06.27 15.58.026]]
27/06/12 15:58:27,418 [main] TimeServer response [NEW time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.58.027]]
27/06/12 15:58:27,926 [main] TimeServer response [NEW time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.58.027]]
27/06/12 15:58:25,375 [main] TimeServer response [time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.58.025]]
27/06/12 15:58:25,884 [main] TimeServer response [time at airport, code:SVO(Moscow Sheremetyevo Airport) [GMT+03:00 2012.06.27 15.58.025]]
27/06/12 15:58:26,393 [main] TimeServer response [time at airport, code:CDG(Paris-Charles de Gaulle Airport) [GMT+02:00 2012.06.27 14.58.026]]
27/06/12 15:58:26,902 [main] TimeServer response [NEW time at airport, code:SVO(Moscow Sheremetyevo Airport) [GMT+03:00 2012.06.27 15.58.026]]
27/06/12 15:58:27,418 [main] TimeServer response [NEW time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.58.027]]
27/06/12 15:58:27,926 [main] TimeServer response [NEW time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.58.027]]
Обновление AirportInfoService bundle
Тестовый сценарий
- запускаем контейнер с установленными bundle
- проверяем состояние bundle (команда ss)
- выполняем запрос от клиента
- изменим строку возвращаемую AirportInfoProvider
- выполняем обновление AirportInfoService bundle (команда update)
- выполняем запрос от клиента - значение не меняется
- выполняем обновление AirportInfoService bundle (команда refresh)
- выполняем запрос от клиента - значение меняется на новое
- проверяем состояние bundle (команда ss)
Выполнение тестового сценария
- запускаем контейнер с установленными bundle
- проверяем состояние bundle (команда ss)
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
48 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
49 ACTIVE WSTimeServer_1.0.0.qualifier
51 ACTIVE TimeService_1.0.4.qualifier
52 ACTIVE TimeServiceInterface_1.0.0.qualifier
53 ACTIVE AirportInfoService_1.0.4.qualifierОбращаем внимание на номер версии AirportInfoService - 1.0.4 - выполняем запрос от клиента
27/06/12 15:44:30,172 [main] TimeServer response [NEW time at airport, code:LGW(London Gatwick Airport) [GMT+00:00 2012.06.27 12.44.030]] - изменим строку возвращаемую AirportInfoProvider
В классе AirportInfoProvider меняем строку:
"RIX", "Riga Airport"));
на
"RIX", "NEW Riga Airport"));В файле MANIFEST.MF меняем номер версии на 1.0.5 - выполняем обновление AirportInfoService bundle (команда update)
osgi> update 53
В логе ничего нет, bundle не содержит активатора - выполняем запрос от клиента - значение не меняется
27/06/12 15:48:59,767 [main] TimeServer response [NEW time at airport, code:RIX(Riga Airport) [GMT+03:00 2012.06.27 15.48.059]] - выполняем обновление AirportInfoService bundle (команда refresh)
osgi> refresh 53
osgi> Event 'UNREGISTERING'(4) for service [{lv.nixx.osgi.sample.serviceinterface.TimeService}={description=TimeService, provide service to get time in airport by code, service.id=42}] is fired
'TimeService' bundle stoped
Event 'REGISTERED' (1) for service [{lv.nixx.osgi.sample.serviceinterface.TimeService}={description=TimeService, provide service to get time in airport by code, service.id=43}] is fired
'TimeService' bundle started
По логу видим, что обновился зависимый bundle - TimeService - выполняем запрос от клиента - значение меняется на новое
27/06/12 15:51:29,353 [main] TimeServer response [NEW time at airport, code:RIX(NEW Riga Airport) [GMT+03:00 2012.06.27 15.51.029]]Значение, возвращаемое клиенту - поменялось
- проверяем состояние bundle (команда ss)
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
2 ACTIVE osgi.cmpn_4.2.0.200908310645
48 ACTIVE cxf-dosgi-ri-singlebundle-distribution_1.3.0
49 ACTIVE WSTimeServer_1.0.0.qualifier
51 ACTIVE TimeService_1.0.4.qualifier
52 ACTIVE TimeServiceInterface_1.0.0.qualifier
53 ACTIVE AirportInfoService_1.0.5.qualifierНомер версии AirportInfoService - изменился на 1.0.5

Комментариев нет:
Отправить комментарий