Статьи

OSGi Service Hook для регистрации всех сервисных вызовов с использованием динамического прокси

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

Но благодаря сервисным хукам , представленным в выпуске 4.2 Osgi , мы можем сделать это простым способом.

Сервисные крючки

Сервисные хуки — это второе лицо OSGi :). Поскольку мы знаем, как получать и прослушивать сервисы, хуки предоставляют способ узнать и управлять, чьи пакеты ожидают или используют сервисы. Цель этого решения — представить 
сценарии использования служб FindHook и 
EventHook для регистрации всех вызовов служб.

Описание решения

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

Решение

— Интерфейс EventHook имеет только один метод для реализации:

 @Override    public void event(ServiceEvent event, Collection contexts) {
final ServiceReference serviceReference = event.getServiceReference();
System.out.println("" + serviceReference.getBundle().getSymbolicName());

if (serviceReference.getProperty(PROXY) == null && serviceReference.getBundle().getBundleContext() != bc) {
Bundle bundle = serviceReference.getBundle();

switch (event.getType()) {
case REGISTERED: {

String[] propertyKeys = serviceReference.getPropertyKeys();
Properties properties = buildProps(propertyKeys, event);
String[] interfaces = (String[]) serviceReference.getProperty(
"objectClass");

Class[] toClass = toClass(interfaces, bundle);
proxyService(bundle,
toClass,
properties,
this.getClass().getClassLoader(), new LoggerProxy(
bc, serviceReference));



break;
}
case UNREGISTERING: {
//TODO
break;
}
case MODIFIED:
case MODIFIED_ENDMATCH: {
//TODO
break;
}
}
}
}

Перед доставкой события этот метод будет вызван. Мы проверяем, если сервис еще не прокси. Если это новый сервис, мы создаем еще один, используя его объявленные интерфейсы, все его свойства и другое свойство «прокси».

 

— Интерфейс FindHook имеет только один метод для реализации

 @Override    public void find(BundleContext bc, String name, String filter,
boolean allServices, Collection references) {
try {
if (this.bc.equals(bc) || bc.getBundle().getBundleId() == 0) {
return;
}

System.out.println(
" bundle : [" + bc.getBundle().getSymbolicName() + "] try to get reference of " + name);
Iterator iterator = references.iterator();

while (iterator.hasNext()) {
ServiceReference sr = (ServiceReference) iterator.next();

System.out.println(
"from bundle" + sr.getBundle().getSymbolicName());

if (sr.getProperty("proxied") == null) {
iterator.remove();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

Этот метод вызывается во время операции поиска службы, в этом обратном вызове метода мы скрываем все службы, которые не проксируются.

LoggerProxy — это пример прокси-сервера для регистрации всех вызовов и переадресации вызова на эффективную ссылку.


public class LoggerProxy implements InvocationHandler, Serializable {
private ServiceReference serviceReference;
private BundleContext bundleContext;

public LoggerProxy(BundleContext bundleContext,
ServiceReference serviceReference) {
this.serviceReference = serviceReference;
this.bundleContext = bundleContext;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-->Methode : [" + method.getName() + "] ");
System.out.println("-->Parameters : ");
for (Object object : args) {
System.out.print("->"+object + " : ");
}
System.out.println("");
Object invoke = method.invoke(bundleContext.getService(serviceReference),
args);

System.out.println("-->Return : " + invoke);
return invoke;
}
}

В конце мы регистрируем наши услуги, используя Активатор:


public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
LoggerHooks loggerHooks = new LoggerHooks(context);
context.registerService(new String[]{FindHook.class.getName(), EventHook.class.getName()},
loggerHooks, null);

}

@Override
public void stop(BundleContext context) throws Exception {
}
}

Консольный выход

Когда начинается пакет 34, он пытается получить услугу TService из пакета 33

osgi> start 34
bundle : [demo.client_0.1] try to get reference of com.jtunisie.akel.demo.api.TService
from bundledemo.register_0.1
from bundledemo.register_0.1
-->Methode : [echo]
-->Parameters :
->Service Found :
-->Return : Yes !Service Found
Yes !Service Found

Пакет 33 регистрирует две службы, одна из которых проксируется

osgi> b 33demo.register_0.1_0.0.0 [33]
Id=33, Status=ACTIVE Data Root=/home/sst/dev/4.2hooks/configuration/org.eclipse.osgi/bundles/33/data
Registered Services
{com.jtunisie.akel.demo.api.TService}={service.id=44}
{com.jtunisie.akel.demo.api.TService}={service.id=45, proxied=true}
No services in use.
Exported packages
com.jtunisie.akel.demo.service.impl; version="0.0.0"[exported]
Imported packages
com.jtunisie.akel.demo.api; version="0.0.0"
org.osgi.framework; version="1.5.0"
scala; version="0.0.0"
No fragment bundles
Named class space
demo.register_0.1; bundle-version="0.0.0"[provided]
No required bundles

Conclusio

Динамические прокси и сервисные хуки предоставляют мощный инструмент для управления сложными сценариями использования OSGi. В этом посте показан один вариант использования с использованием перехватов find и event для регистрации всех вызовов службы. Во втором посте я постараюсь представить
Listener Hook и способ прозрачного распространения сервиса Osgi.

исходный код находится в разделе svn: svn checkout
http://osgiservicelogger.googlecode.com/svn/trunk/ osgiservicelogger-только для чтения