вторник, 12 октября 2010 г.

Введение в OSGi - Снова о доступности классов


Шпаргалка по механизму, обеспечивающему доступность классов в OSGi.

1. Один класслоадер на бандл

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

Данное решение имеет следствия:
- Classpath больше не линеен - у каждого бандла он свой.
- Классическая иерархия класслоадеров (Bootstrap -> Extension -> System) не работает.
- По-умолчанию в качестве родительского используется Bootstrap classloader, но данное поведение настраивается.

2. Каждый класс загружается однажды и только класслоадером бандла, в котором он определен

Если класс определен в бандле A, а объект данного класса впервые создается в бандле B, то загружен класс будет класслоадером бандла A. Данное поведение, в частности, является источником проблемы подробно описанной в статье Проблемы загрузки классов.

Соответственно, если объекты некоего класса, определенного в бандле A, создаются и в бандле A, и в бандле B, то это будут объекты одного и того же класса.

Классы, определенные в бандле A, доступны для использования в бандле B, если они экспортируются из бандла A:

Export-Package: packageA

и импортируются бандлом B:

Import-Package: packageA.

Если пакет импортируется бандлом B, но не экспортируется бандлом A - бандл B будет находиться в состоянии INSTALLED, создание объектов классов, определенных в бандле A, в нем будет невозможно.

Если пакет не импортируется бандлом B, то при создании объекта класса, определенного в бандле A, будет брошено ClassNotFoundException.

3. Совместимость версий пакетов

Не только бандлы, но и пакеты имеют версию. Версия назначается пакету при определении экспорта в манифесте бандла:

Export-Package: mypackageA; version="1.0.0"

Классы, определенные в пакетах, имеющих разный номер версии, являются разными и несовместимыми друг с другом.


ExportPackage: mypackageA; version="1.0.0"
ImportPackage: mypackageA; version="1.0.0"
A a1 = new A();



ExportPackage: mypackageA; version="2.0.0"
ImportPackage: mypackageA; version="2.0.0"
A a2 = new A();


a1 и a2 - объекты разных классов.

4. Полиморфизм и наследование осуществляются как обычно

В бандле A определен интерфейс IA: public interface A{}

В бандлах B и C определены классы, реализующие интерфейс A:
public class B impl A{}; public class C impl A{}

В бандле D регистрируем сервис: A myA = createServiceA();.

Статический тип переменной myA - A, во время исполнения типом переменной myA может быть B или C.

5. Причины возникновения ClassNotFoundException

Наиболее частыми причинами возникновения CNFE являются следующие:
- Не все зависимости, объявленные в манифесте бандла, разрешены, т.е. отсутствуют бандлы, от которых зависит данный.
- Тип не видим (не экспортируется или не импортируется)
- Динамически генерируемые классы: Proxy, CGLib, ...
- Сериализация

6. DynamicImport-Package

В манифесте бандла можно описывать динамические зависимости:

Бандл org.hibernate:
DynamicImport-Package: *

Бандл A:
Import-Package: org.hibernate
Export-Package: mypackageA

Особенности
- Не возникает ошибок, если зависимость не может быть разрешена
- Допустимо использовать символ подстановки - звездочку, обозначающую "все"
- Данная возможность должна использоваться как последнее оружие, если никакое другое решение невозможно.

7. Equinox buddy loading

Подробно данная специфичная для Equinox возможность описана в статье.

Бандл org.hibernate:
Eclipse-BuddyPolicy: registered

Бандл A:
Eclipse-RegisteredBuddy: org.hibernate

Теперь Хибернейт может выполнить loadClass("A");.

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

8. Последовательность загрузки классов

- родительский загрузчик из java.-пакета
- родительский загрузчик из boot-delegation-пакетов
- загрузчик из импортируемых пакетов
- загрузчик из required-бандлов
- загрузчик из собственного classpath бандла
- загрузчик из динамических импортов
- загрузчик через buddy-loading.

9. Сборка мусора в OSGi

Объекты классов, загруженных класслоадером бандла не уничтожаются сразу после того, как бандл остановлен или удален. Необходимо обеспечить отсутствие ссылок на данные объекты, например явно установить все созданные переменные в null в методе stop активатора бандла.

10. Classloading Hooks

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

Примеры:
- Модификация байткода при загрузке классов
- Перехват доступа к данным бандла.

Некоторые возможности Equinox основаны на данном механизме, например обработка атрибута Eclipse-LazyStart.

Рекомендую

Презентацию Martin'а Lippert'а - Classloading and Type Visibillity in OSGi.

Понравилось сообщение - подпишитесь на блог

3 комментария:

mvolkov комментирует...

Неточность:
В 10 пункте описаны classloading hooks, которые присутствуют только в equinox. А в заголовке написано про osgi. В спецификации osgi 4.2 для этих целей используется механизм URL Handlers Service, через который можно подменить любой ресурс bundle, в том числе и байткод классов. Видимо equinox использует именно этот механизм для реализации своих classloading hooks.

maratische комментирует...

Вопрос начинающего, реализаций OSGi много, что лучше посмотреть для небольшого swing приложения?

как обычно есть клевая идея, надо набросать прототип и показать коллегам/начальству, но мало времени :)

опыта с OSGi нет совсем, вот начал гуглить :)

Pavel Samolisov комментирует...

Я для себя выбрал Equinox, с другими реализациями практически не работал.

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

Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!