Статьи

Hessian Client Extender с динамическим прокси внутри OSGi


Следующая версия OSGi (4.2) содержит множество улучшений, таких как перехват регистрации реестра, транзакции и распространение.
Apache CXF и
Eclipse Riena являются пионерами, использующими это для обеспечения распространения внутри OSGi.
Ньютон в качестве реализации SCA и других фреймворков использовал пока что удаленное взаимодействие внутри OSGI текущей версии (4.1).

Вступление

В
предыдущем посте я рассказал, как зарегистрировать веб-сервис Hessian декларативным способом, связав три экстендера. Теперь я попытаюсь представить, как ссылаться на него из комплекта удаленных клиентов, используя модель декларации SCA, принятую в выпуске 4.2.

Для этого я использовал:

  1. Динамический Проксис
  2. DynamicImport-пакет
  3. Расширитель комплектов
  4. Stax XML парсер 
  5. Гессенский клиент

Начну с конца:

Я — гессианский клиент

Клиентский пакет содержит один активатор для отслеживания ссылки на com.jtunisie.osgi.hessian.IService.

package com.jtunisie.osgi.hessian.client.test;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

private RemoteServieTracker remoteServiceTracker;

@Override
public void start(BundleContext context) throws Exception {
remoteServiceTracker = new RemoteServieTracker(context);
remoteServiceTracker.open();
String execute = remoteServiceTracker.execute();
System.out.println(execute);
}

@Override
public void stop(BundleContext context) throws Exception {
if (remoteServiceTracker != null) {
remoteServiceTracker.close();
}
}
}


package com.jtunisie.osgi.hessian.client.test;

import com.jtunisie.osgi.hessian.IService;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;


public class RemoteServieTracker extends ServiceTracker implements IService {

public RemoteServieTracker(BundleContext context) {
super(context, IService.class.getName(), null);
}

@Override
public String execute() {
IService service = (IService) getService();
String execute = service.execute();
return execute;
}
}

Запрашиваемая услуга не зарегистрирована в текущем реестре. Чтобы получить это:

  1. Добавьте файл remote-services.xml в каталог OSGI-INF / remote-service:
<?xml version="1.0" encoding="UTF-8"?>

<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
<service-description>
<provide interface="com.jtunisie.osgi.hessian.IService" />
<property name="osgi.remote.interfaces">*</property>
<property name="osgi.remote.configuration.type">pojo</property>
<property name="osgi.remote.configuration.pojo.address">http://localhost:8082/jtunisie</property>
</service-description>

</service-descriptions>

 2- Добавьте следующую строку в файл MANIFEST.MF: 

Hessian-File: OSGI-INF / remote-service / remote-services.xml

Мы закончили наш тестовый клиентский пакет. Давайте начнем наш расширитель. Идея состоит в том, чтобы зарегистрировать прокси-сервис, который будет скрывать удаленный доступ, как это могут делать фреймворки.

II- STAX Parser:

Я использовал STAX для анализа файла remote-services.xml:

package com.jtunisie.osgi.hessian.client;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.XMLEvent;
import org.osgi.framework.ServiceRegistration;

/**
*
* @author slim
*/
public class Parser {

public static List<Pair> parseRemoteconfig(URL url) {


// Create event reader
InputStream openStream = null;
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
// Setup a new eventReader
openStream = url.openStream();

XMLEventReader eventReader = factory.createXMLEventReader(openStream);
QName name = new QName("name");
QName iname = new QName("interface");
List<Pair> remotes = null;
String remotInterface = "";
String remoteAdress = "";

while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();



if (event.isStartElement()) {

if (event.asStartElement().getName().getLocalPart().equals("service-descriptions")) {
remotes = new ArrayList<Pair>();
}
if (event.asStartElement().getName().getLocalPart().equals("service-description")) {
}
if (event.asStartElement().getName().getLocalPart().equals("provide")) {
Attribute attributeByName = event.asStartElement().getAttributeByName(iname);
if (attributeByName != null) {
remotInterface = attributeByName.getValue();
}
}
if (event.asStartElement().getName().getLocalPart().equals("property")) {

Attribute attributeByName = event.asStartElement().getAttributeByName(name);
if (attributeByName != null && attributeByName.getValue().equals("osgi.remote.configuration.pojo.address")) {
remoteAdress = eventReader.nextEvent().asCharacters().getData();
}
}


}
if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart().equals("service-descriptions")) {
return remotes;
}
if (event.asEndElement().getName().getLocalPart().equals("service-description")) {
Pair p = Pair.getInstence(remotInterface, remoteAdress);
remotes.add(p);
}
}
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (XMLStreamException ex) {
ex.printStackTrace();
} finally {
try {
openStream.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
throw new RuntimeException("Parse Exception");
}

public static class Pair {

private String remoteInterface;
private String remoteAdress;
private Class<?> clazz;
private ServiceRegistration serviceRegistration;

static Pair getInstence(String remotInterface, String remoteAdress) {
try {
return new Pair(remotInterface, remoteAdress);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}

private Pair(String remotInterface, String remoteAdress) throws ClassNotFoundException {
this.remoteInterface = remotInterface;
this.remoteAdress = remoteAdress;
this.clazz = Class.forName(remotInterface);
}

public Class<?> getClazz() {
return clazz;
}

public String getRemoteInterface() {
return remoteInterface;
}

public String getRemoteAdress() {
return remoteAdress;
}

public ServiceRegistration getServiceRegistration() {
System.out.println("Service registred !!");
return serviceRegistration;
}

public void setServiceRegistration(ServiceRegistration serviceRegistration) {
this.serviceRegistration = serviceRegistration;
}
}
}

III- слушатель пакета расширения

1- Прослушайте обновленный и установленный пакет

2 — Проверьте файл MANIFEST.MF

3 — Получите URL-адрес и выполните синтаксический анализ ресурса XML

4 — Зарегистрируйте службу прокси

package com.jtunisie.osgi.hessian.client;

import com.jtunisie.osgi.hessian.client.Parser.Pair;
import java.net.URL;
import java.util.Dictionary;
import java.util.List;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.SynchronousBundleListener;
import static org.osgi.framework.BundleEvent.*;

/**
*
* @author slim
*/
public class BundleListner implements SynchronousBundleListener {

private static final String HESSIAN_HEADER = "Hessian-File";
private final BundleContext context;

public BundleListner(BundleContext context) {
this.context = context;
}

@Override
public void bundleChanged(BundleEvent event) {
if (event.getType() == UPDATED || event.getType() == STARTED) {

try {
addBundle(event.getBundle());
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
if (BundleEvent.STOPPED == event.getType()) {
removeBundle(event.getBundle());
}
}

private void addBundle(Bundle bundle) throws ClassNotFoundException {
@SuppressWarnings("unchecked")
Dictionary<String, String> headers = bundle.getHeaders();
String indexPath = headers.get(HESSIAN_HEADER);

if (indexPath == null) {
return;
}
URL resource = bundle.getResource(indexPath);
if (resource == null) {
return;
}

List<Pair> remotes = Parser.parseRemoteconfig(resource);
if (remotes != null) {
for (Pair pair : remotes) {
pair.setServiceRegistration(new ClientExtender().registerService(context, pair));
}
}
}

private void removeBundle(Bundle bundle) {
// TODO
}
}

IV-DynamicImport-Package:

Поскольку этот расширитель будет манипулировать неизвестным классом и интерфейсами, мы должны включить DynamicImport-Package для всех пакетов. Для этого добавьте <DynamicImport-Package> * </ DynamicImport-Package> в файл pom.

V- Динамический Проксис

1 -Создать универсальный прокси-класс GenericClientProxy для переадресации вызовов на удаленную реализацию в соответствии с запрошенной службой и назначением:

package com.jtunisie.osgi.hessian.client;

import com.caucho.hessian.client.HessianProxyFactory;
import com.jtunisie.osgi.hessian.client.Parser.Pair;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
*
* @author slim
*/
public class GenericClientProxy implements InvocationHandler, Serializable {

private Pair pair;

public GenericClientProxy(Pair pair) throws ClassNotFoundException {
this.pair = pair;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HessianProxyFactory factory = new HessianProxyFactory();
Serializable service = (Serializable) factory.create(pair.getClazz(), pair.getRemoteAdress());
return method.invoke(service, args);
}
}

2- Класс ClientExtender зарегистрирует этот прокси внутри локальной OSGi, как если бы он присутствовал.

package com.jtunisie.osgi.hessian.client;

import com.jtunisie.osgi.hessian.client.Parser.Pair;
import java.lang.reflect.Proxy;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

/**
*
* @author slim
*/
public class ClientExtender {

ServiceRegistration registerService(BundleContext context, Pair pair) throws ClassNotFoundException {
String _interface = pair.getRemoteInterface();
Class[] interfaces = {pair.getClazz()};
GenericClientProxy clientProxy = new GenericClientProxy(pair);
Object newProxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, clientProxy);
return context.registerService(_interface, newProxyInstance, null);
}

void unregisterService(BundleContext context, Pair pair) {
//TODO
}
}

VI- Исполнение

Если мы запустим комплект расширителей и тестовый клиент, то увидим, что наш расширитель зарегистрирует удаленный сервис в локальном реестре.

osgi> b <extender-id>
........
Id=41, Status=ACTIVE Data Root=/home/slim/work/svn/hessianextender-read-only/env.client/configuration/org.eclipse.osgi/bundles/41/data
Registered Services
{com.jtunisie.osgi.hessian.IService}={service.id=47}
No services in use.
Exported packages
com.caucho.hessian.security; version="0.0.0"[exported]
com.caucho.hessian.io; version="0.0.0"[exported]
com.caucho.hessian.client; version="0.0.0"[exported]
com.caucho.hessian.server; version="0.0.0"[exported]
com.caucho.hessian.jmx; version="0.0.0"[exported]
com.caucho.hessian.mux; version="0.0.0"[exported]
com.caucho.hessian.util; version="0.0.0"[exported]
com.caucho.hessian.micro; version="0.0.0"[exported]
com.caucho.hessian; version="0.0.0"[exported]
com.caucho.hessian.test; version="0.0.0"[exported]
Imported packages
javax.crypto; version="0.0.0"<System Bundle [0]>
javax.management; version="0.0.0"<System Bundle [0]>
javax.naming; version="0.0.0"<System Bundle [0]>
javax.naming.spi; version="0.0.0"<System Bundle [0]>
javax.servlet; version="2.5.0"<initial@reference:file:plugins/web/pax-web-service-0.4.1.jar/ [37]>
javax.servlet.http; version="2.5.0"<initial@reference:file:plugins/web/pax-web-service-0.4.1.jar/ [37]>
javax.xml.namespace; version="0.0.0"<System Bundle [0]>
javax.xml.stream; version="0.0.0"<System Bundle [0]>
javax.xml.stream.events; version="0.0.0"<System Bundle [0]>
org.osgi.framework; version="1.4.0"<System Bundle [0]>
org.w3c.dom; version="0.0.0"<System Bundle [0]>
No fragment bundles
Named class space
com.jtunisie.osgi.hessian.client; bundle-version="1.0.0"[provided]
No required bundles

VII — Заключение

Использование экстендеров в OSGi — мощный инструмент. Этот пост обрисовывает в общих чертах:

 образец расширителя.

 Bundle слушатель.

 Динамический прокси для удаленного взаимодействия.

 Stax XML анализирует

 гессенский сервисный клиент.

Это не для производства :), а как дидактическое использование. Если вы хотите мощный API, проверьте Eclipse Riena.

Исходный код доступен по ссылке svn: svn checkout
http : //hessianclient.googlecode.com/svn/trunk/ hessianclient-только для чтения