Избавьтесь от головной боли из-за подключаемых модулей Eclipse при помощи OSGi

Использование Open Services Gateway Initiative API для исключения зависимостей подключаемых модулей Eclipse

Узнайте, как писать расширения в коде для других подключаемых модулей, не создавая двоичной зависимости от этих модулей, при помощи API динамических расширений Eclipse V3.2. Решите эти и другие задачи с API служб Open Services Gateway Initiative (OSGi) и динамическими API.

Боб Балфе, старший инженер-программист и руководитель проекта, Lotus

Боб Балфе (Bob Balfe) работает старшим инженером-программистом в IBM и архитектором клиентского приложения, управляемого порталом, в группе WebSphere Everyplace Deployment.



Чак Императо, инженер-консультант по программному обеспечению, IBM

Чак Императо (Chuck Imperato) работает инженером-консультантом по программному обеспечению в IBM и разработчиком в группе WebSphere Everyplace Deployment.



11.04.2006

В этой статье приведен пример одного подключаемого модуля, принимаемого в XML для регистрации расширений определенной точки расширения. Мы выполним это полное развязывание компонентов при помощи подключаемых модулей Extension Registry и предоставления OSGi-службы.

Подключаемые модули, точки расширения, OSGi

Как известно, компонентная архитектура Eclipse основана на использовании подключаемых модулей. Это означает, что фрагменты кода объединяются в один компонент, и этот компонент регистрируется в среде Eclipse как один из компонентов, с которым могут связаться или который могут вызвать другие компоненты. Точки расширения - это способ разрешения в подключаемом модуле реализации дополнительной функциональности другими модулями. Теперь возьмем и объединим все это в управляемую среду времени исполнения, в которой подключаемые модули могут появляться и исчезать динамически. Так, по существу, мы получим OSGi.

Наш пример подключаемого модуля

Начнем с основного подключаемого модуля, предоставляющего точку расширения для регистрации нового отображения строк для службы поиска синонимов. Эта служба позволяет другим зарегистрировать слово и отобразить его в другое слово (синоним). Основное расширение содержит очень простые элементы: одно слово и, конечно же, новый синоним. Основные конструкции это подключаемого модуля показаны в таблице 1.

Таблица 1. Элементы нашего примера подключаемого модуля
Имя подключаемого модуляcom.company.SynonymRegistry
Точка расширенияSynonym
Элементыword - Слово, для которого вы хотите добавить синоним
synonym - Синоним, который вы хотите зарегистрировать

Мы также зарегистрируем подключаемый модуль в качестве OSGi-службы. Это означает, что он будет загружаться только при явном указании и будет доступен другим потребителям. Другие потребители должны знать только Interface и имя класса OSGi для того, чтобы использовать службу. В нашем примере мы никогда реально не вызываем службу из-за предоставленной точки расширения. Мы используем OSGi API для предупреждения о том, что служба появляется и работает, чтобы мы могли корректно зарегистрировать наши расширения.

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

Промежуточный подключаемый модуль

Следующим подключаемым модулем будет модуль стороннего поставщика, который знает о широко известной службе и точке расширения, но не хочет соединяться с этим подключаемым модулем, потому что тогда он будет зависеть от него при разрешении во время исполнения. Это означает, что подключаемый модуль может существовать на машине, на которой адресуемый подключаемый модуль (com.company.SynonymRegistry) может не существовать. Поскольку теперь мы живем в мире OSGi и динамических систем времени исполнения, то хотим убедиться, что наш подключаемый модуль может работать без возникновения аварии или ошибки времени исполнения. Наш промежуточный модуль будет брать XML-файл с синонимами и регистрировать каждый из них с подключаемым модулем SynonymRegistry путем использования предоставленной точки расширения.

Листинг 1. Пример XML-файла для подтверждения концепции
Synonyms.xml
<?xml version="1.0" encoding="UTF-8"?>
<synonyms>
	<entry word="mediator" synonym="broker"/>
	<entry word="mediator" synonym="go-between"/>
	<entry word="mediator" synonym="interceder"/>
	<entry word="mediator" synonym="intermediary"/>
<synonyms>

Первым действием, которое промежуточный модуль делает в своем методе start(), является регистрация с помощью OSGi-службы в качестве прослушивателя инициализаций службы. Мы вызываем метод OSGi-службы addServiceListener() с объектом BundleContext, переданным в метод start(). Приведенный ниже код показывает пример вызова этого API путем передачи this и ID службы, в которой мы заинтересованы.

context.addServiceListener( this, "com.company.SynonymRegistry" );

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

Вы можете поинтересоваться, зачем нам надо это делать. Ответ находится в последовательности начального запуска. В мире OSGi мы не всегда знаем, когда доступна другая служба, и, в результате, мы должны принимать это во внимание. Регистрируясь в качестве прослушивателя службы, мы получаем уведомление о старте и останове этой службы. Это позволяет нам кэшировать наши синонимы, если служба недоступна. Когда она становится доступной, мы получаем уведомление и регистрируем наши расширения.

Регистрация новых расширений

Наконец мы дошли до сути этой статьи. Теперь мы имеем данные (Synonyms.xml) и хорошо известную точку расширения (com.company.SynonymRegistry. Synonym), для которой хотим предоставить динамические расширения. Поскольку мы не знаем, когда наш подключаемый модуль будет инициализирован, и инициализирован ли подключаемый модуль Synonym, мы просто попытаемся зарегистрировать записи нашего XML-файла при загрузке нашего подключаемого модуля. Помните: это всего лишь пример, демонстрирующий концепции; он не должен быть реализован в подобного рода коде для конечного продукта. Обычно мы делали бы насколько возможно большую часть инициализации в "ленивой манере" (с задержкой, или при необходимости).

Новой функциональной возможностью в Eclipse V3.2 является способность предоставлять расширения во время исполнения. Например, можно написать приложение, содержащее окно просмотра, которое создает перспективу при нажатии кнопки. Перспектива добавляется в реестр расширений и может быть показана в списке доступных перспектив. Самым значительным преимуществом этой способности является то, что она может ослабить множество "жестких" зависимостей между подключаемыми модулями. Plug-in А может внести свой вклад в платформу, определенную в Plug-in B, не завися от Plug-in B. Более того, комбинируя это со средой OSGi, подключаемый модуль может проверить существование службы и, при положительном ответе, создать расширение из точки расширения, определенной в службе. Это способствует созданию истинно динамической среды, используя, в то же время, принципы ориентированной на службы архитектуры.

В Eclipse V3.2 представлен новый метод addContribution(), определенный в интерфейсе IExtensionRegistry. Код в листинге 2 демонстрирует, как можно добавить расширение при помощи addContribution() API. Метод addContribution() спроектирован на получение простого XML как InputStream в первом параметре.

Листинг 2. Как выражение может быть добавлено через addContribution() API
IExtensionRegistry registry = RegistryFactory.getRegistry( );

Object key = ((ExtensionRegistry) registry).getTemporaryUserToken( );

ByteArrayInputStream is = 
		new ByteArrayInputStream( buffer.toString().getBytes() );
		
try {
	registry.addContribution(is, bundle, null, null, key);	
}	
finally {	
try {
		is.close( );
	}catch (IOException e) {
				
	}
}

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

Object key = ((ExtensionRegistry)registry).getTemporaryUserToken();

Однако в следующей промежуточной версии Eclipse V3.2 этот маркер не доступен. Для поддержки динамических расширений в вашем приложении наша запускающая программа должна будет предоставить следующую настройку виртуальной машине:

-Declipse.registry.nulltoken=true

Это определение теперь позволяет нам использовать null как User Token в addContribution() API. Наш код будет выглядеть аналогично приведенному ниже. Обсуждение этой проблемы приведено в Bugzilla-списке ошибок (багов).

Листинг 3. getTemporaryUserToken() в действии
...
try {
	registry.addContribution(is, bundle, null, null, null);	

}	
...

Показанная выше переменная buffer представляет реальный XML-блок. Этот XML - это точная копия того, что мы можем увидеть в файле plugin.xml. Возвращаясь к нашему примеру SynonymRegistry, XML для этого расширения мог бы выглядеть примерно как в листинге 4.

Листинг 4. XML для SynonymRegistry
<plugin>
	<extension point="com.company.synonymregistry" id="myExtension">
		<synonyms
			word="mediator"
			synonym="broker"/>
	</extension>
</plugin>

Можно представить себе создание охватывающего (wrapper) класса фабрики, который принимает такие аргументы как ID точки расширения, ID расширения, имя элемента (в данном случае, synonyms), реальные атрибуты и ID содействующего фрагмента. Охватывающий класс заботится о форматировании аргументов в XML-строку, похожую на указанный выше код. Эта XML-строка читается затем в ByteArrayInputStream для передачи в метод addContribution() интерфейса IExtensionRegistry. Единственными другими аргументами для этого метода являются маркер пользователя и ID фрагмента. Важно отметить, что этим ID фрагмента должен быть ID того фрагмента, который выполняет расширение, а не того, в котором определена точка расширения.

Предостережения и советы

Одним из интересных моментов, появившихся в M5, версии Eclipse от 17 февраля 2006 года, является то, что вызов addContributions() является асинхронным. Это означает, что расширения не будут доступны немедленно, поскольку Eclipse начинает работу по выполнению реальных регистраций. Короче говоря, вы должны начать свою собственную работу и синхронизироваться с ней для получения синхронного поведения.

Вот три совета, как упростить эту задачу:

  • Создайте новое задание, которое регистрирует себя в качестве RegistryChangeListener.
  • При выполнении задания убедитесь в том, что ваш код задания слушает isRegistered в функции обратного вызова RegistryListener.
  • Выйдите из задания, как только все ваши регистрации будут выполнены.

Конечно же, теперь мы должны соединить наш вызывающий код с нашим порожденным заданием для получения синхронного вызова. Это обосновано только тогда, когда ваш код требует немедленного использования расширений. Надеюсь, что ваш код спроектирован для "ленивой" работы, и такая инициализация не важна.

Резюме

Использование динамических расширений может быть создано программным способом. Динамические расширения усиливают декомпозицию, используя среду OSGi для прослушивания доступности служб (загружены или нет). Совместное использование этих технологий разрешит декларативные дополнения и 100-процентное развязывание компонентов.

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=146213
ArticleTitle=Избавьтесь от головной боли из-за подключаемых модулей Eclipse при помощи OSGi
publish-date=04112006