Это может быть совершенно сложно из-за правил разрешения пакетов OSGI. Прочтите эту статью — я нашел, что это хорошее объяснение различных применимых правил. В частности, ознакомьтесь с директивой uses
: http://www.christianposta.com/blog/?p=241
Жизнь будет намного проще, если у обоих будет возможность использовать одну и ту же версию (но я знаю, что это не всегда возможно).
Для каждого объявления пакета Import-Package должен быть соответствующий Export-Package с тем же пакетом.
Пакеты также могут прикреплять другие атрибуты к пакетам, которые он импортирует или экспортирует. Что, если мы добавим в наш пример атрибут версии:
Bundle-Name: Bundle A Import-Package: org.apache.foo;version="1.2.0"
Это означает, что пакет A зависит от пакета org.apache.foo с минимальной версией 1.2.0. Да, вы правильно прочитали. Хотя с OSGI вы можете указать диапазон версий, если вы не укажете диапазон, а вместо этого используете фиксированную версию, это приведет к значению «минимум» фиксированного значения. Если для того же пакета существует более поздняя версия, будет использоваться более поздняя версия. Таким образом, пакет A не будет правильно разрешен, если не будет соответствующего пакета B, который экспортирует требуемый пакет:
Bundle-Name: Bundle B Export-Package: org.apache.foo;version="1.2.0"
Обратите внимание, что обратное неверно... Если Bundle B экспортирует версию 1.2.0, Bundle A не требуется указывать версию 1.2.0. Он может использовать этот импорт и разрешать просто отлично:
Bundle-Name: Bundle A Import-Package: org.apache.foo
Это связано с тем, что импорт объявляет нужные версии. Экспортированная версия не указывает ничего, что должен использовать импортирующий пакет (это справедливо для любых атрибутов, а не только для версии). Import-Package точно определяет, какая версия (или атрибут) ему нужна, и должен существовать соответствующий Export-Package с тем же атрибутом.
Что произойдет, если у вас есть сценарий, в котором пакет A импортирует пакет и указывает версию, предоставляемую двумя пакетами:
Bundle-Name: Bundle A Import-Package: org.apache.foo;version="1.2.0"
Bundle-Name: Bundle B Export-Package: org.apache.foo;version="1.2.0"
Bundle-Name: Bundle C Export-Package: org.apache.foo;version="1.2.0"
Какой пакет использует Bundle A? Ответ: это зависит от того, какой пакет (B или C) был установлен первым. Пакеты, установленные первыми, используются для удовлетворения зависимости при обнаружении нескольких пакетов с одинаковой версией.
Все может стать немного сложнее при горячем развертывании пакетов после того, как некоторые из них уже были решены. Что, если сначала установить пакет B, а затем попытаться установить вместе пакет A и следующий пакет D:
Bundle-Name: Bundle D Export-Package: org.apache.foo;version="1.3.0"
Как мы видели выше, объявление версии в Bundle A (1.2.0) означает минимальную версию 1.2.0; поэтому, если бы была доступна более высокая версия, она была бы выбрана (версия 1.3.0 из Bundle D в данном случае). Однако это подводит нас к другому временному правилу для разрешения пакетов: уже разрешенные пакеты имеют более высокий приоритет, чем неразрешенные.
Причина этого в том, что структура OSGI склонна отдавать предпочтение повторному использованию данного пакета. Если проблема решена и она нужна новым пакетам, то он не будет пытаться иметь много других версий того же пакета, если в этом нет необходимости. Директива Bundle «использует»
Приведенных выше правил для разрешения пакетов по-прежнему недостаточно, и неправильный класс все еще может использоваться во время выполнения, что приводит к исключению приведения класса или аналогичному. Вы видите, чего может не хватать?
Что, если бы у нас был такой сценарий. Bundle A экспортирует пакет org.apache.foo, содержащий класс FooClass. У FooClass есть метод, который возвращает объект типа BarClass, но BarClass не определен в пространстве классов пакета, он импортируется следующим образом:
1 2 3 открытый класс FooClass { public BarClass execute(){ ... } }
Bundle-Name: Bundle A Import-Package: org.apache.bar;version="3.6.0" Export-Package: org.apache.foo;version="1.2.0"
Пока все в порядке, пока есть другой пакет, который правильно экспортирует org.apache.bar с правильной версией.
Bundle-Name: Bundle B Export-Package: org.apache.bar;version="3.6.0"
Эти две пачки будут нормально решаться. Теперь, если мы установим еще два пакета, Bundle C и Bundle D, которые будут выглядеть так:
Bundle-Name: Bundle C Import-Package: org.apache.foo;version="1.2.0", org.apache.bar;version="4.0.0"
Bundle-Name: Bundle D Export-Package: org.apache.bar;version="4.0.0"
Мы видим, что Bundle C импортирует пакет org.apache.foo из Bundle A. Bundle C может попытаться использовать FooClass из org.apache.foo, но когда он получит возвращаемое значение типа BarClass, что произойдет? Пакет A предполагает использование версии 3.6.0 BarClass, но пакет C использует версию 4.0.0. Таким образом, используемые классы не согласованы в пакетах во время выполнения (т. е. вы можете столкнуться с некоторым типом несоответствия или исключения приведения класса), но все равно все будет нормально разрешаться во время развертывания в соответствии с приведенными выше правилами. Что нам нужно, так это сообщить всем, кто импортирует org.apache.foo, что мы используем классы из определенной версии org.apache.bar, и если вы хотите использовать org.apache.foo, вы должны использовать ту же версию, которую мы импортируем. Это именно то, что делает директива uses. Давайте изменим пакет A, чтобы указать именно это:
Bundle-Name: Bundle A Import-Package: org.apache.bar;version="3.6.0" Export-Package: org.apache.foo;version="1.2.0"";uses:=org.apache.bar
Учитывая новую конфигурацию для Bundle A, пакеты не будут корректно разрешаться сверху. Пакет C не может разрешить, потому что он импортирует org.apache.foo, но ограничение «uses» для пакета A указывает, что C должен использовать ту же версию, что и A (3.6.0) для org.apache.bar, иначе пакет будет не разрешается при попытке развертывания. Решение этой проблемы — изменить версию в Bundle C для org.apache.bar на 3.6.0.