В суровом городе снова морозы, а суровый программист хочет рассказать о том, что такое фрагменты и как с их помощью расширять classpath бандла.
Давайте рассмотрим следующую ситуацию: мы хотим использовать в нашем OSGi-приложении логирование с помощью log4j. Ситуация, кстати, не такая уж и редкая. В проект Orbit входит бандл org.apache.log4j, который инкапсулирует данную библиотеку и экспортирует ее packages. По идее, достаточно добавить данный бандл как зависимость (через Require-Bundle или Import-Package) и наслаждаться жизнью, но не тут то было. Дело в том, что log4j нужно конфигурировать, т.е. в корне classpath должен находиться файл log4j.properties, с примерно таким содержимым:
log4j.rootLogger=warn, stdout, file
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file test.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=test.log
log4j.appender.file.Append=true
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### icqlib logging
log4j.logger.name.samolisov=debug
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file test.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=test.log
log4j.appender.file.Append=true
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### icqlib logging
log4j.logger.name.samolisov=debug
Проблема в следующем: бандл org.apache.log4j ничего не знает о наших бандлах, соответственно, импортировать содержимое нашего classpath (т.е. log4j.properties) он не может.
К счастью, в OSGi существует такое понятие, как фрагментный бандл (или, для краткости, - фрагмент). От обычного бандла данная сущность отличается наличием в манифесте строчки Fragment-Host: с указанием названия и диапазона версий хостового (основного) бандла. Особенность фрагмента заключается в том, что он не может находиться в состоянии Active (и, соответственно, не может иметь активатора) и не имеет отдельного classpath, а расширяет classpath хостового бандла. Т.е., чтобы было понятнее, у хостового бандла и всех его фрагментов classpath общий.
Давайте рассмотрим пример. Создадим обычный бандл name.samolisov.log4j.test. Код его активатора следующий:
package name.samolisov.log4j.test;
import org.apache.log4j.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private static Logger logger = Logger.getLogger(Activator.class);
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
logger.info("bundle's activator has been started");
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
logger.info("bundle's activator has been stoped");
}
}
import org.apache.log4j.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private static Logger logger = Logger.getLogger(Activator.class);
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
logger.info("bundle's activator has been started");
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
logger.info("bundle's activator has been stoped");
}
}
Естественно, данный бандл зависит от org.apache.log4j, поэтому его манифест содержит следующие строки:
Import-Package: org.osgi.framework;version="1.3.0"
Require-Bundle: org.apache.log4j;bundle-version="1.2.13"
Require-Bundle: org.apache.log4j;bundle-version="1.2.13"
Затем создадим фрагмент name.samolisov.log4j.connector. Для создания фрагменов Eclipse PDE предоставляет отдельный визард, вызвать который можно через New -> Project -> Plug-in Development -> Fragment Project. Отличается от обычного он тем, что содержит поле, позволяющее указать значение параметра Fragment-Host: .
В каталог src данного бандла положим файл конфигурации log4j.properties. Но! Этого недостаточно. Чтобы даный файл попал в бинарную сборку плагина, необходимо отметить его галочкой в окошке Binary Build на вкладке Build дизайнера манифеста.
Т.е. файл build.properties будет выглядеть следующим образом:
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
src/log4j.properties
output.. = bin/
bin.includes = META-INF/,\
.,\
src/log4j.properties
Манифест фрагмента приведу полностью:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Connector
Bundle-SymbolicName: name.samolisov.log4j.connector
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Pavel Samolisov
Fragment-Host: org.apache.log4j;bundle-version="1.2.13"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ManifestVersion: 2
Bundle-Name: Connector
Bundle-SymbolicName: name.samolisov.log4j.connector
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Pavel Samolisov
Fragment-Host: org.apache.log4j;bundle-version="1.2.13"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Фрагменты могут расширять classpath не только собственным содержимым, но и за счет определения в них необходимых импортов. Например, наше приложение использует в качестве логера бандл org.apache.commons.logging и мы хотим, чтобы он делегировал логирование log4j. Как сделать это применительно к Spring я уже писал. В данном случае, проблема заключается в том, что бандл org.apache.commons.logging не импортирует никакие packages бандла org.apache.log4j. Решить данную проблему можно с помощью фрагмента с таким манифестом:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: CLConnector
Bundle-SymbolicName: name.samolisov.commons_logging.connector
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Pavel Samolisov
Fragment-Host: org.apache.commons.logging;bundle-version="1.0.4"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.apache.log4j;version="1.2.13",
org.apache.log4j.config;version="1.2.13",
org.apache.log4j.helpers;version="1.2.13"
Bundle-ManifestVersion: 2
Bundle-Name: CLConnector
Bundle-SymbolicName: name.samolisov.commons_logging.connector
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Pavel Samolisov
Fragment-Host: org.apache.commons.logging;bundle-version="1.0.4"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.apache.log4j;version="1.2.13",
org.apache.log4j.config;version="1.2.13",
org.apache.log4j.helpers;version="1.2.13"
(естественно, при необходимости можно импортировать и другие packages).
Собственно, это - все, что я хотел рассказать о фрагментах. Применительно к логированию же скажу, что использование log4j или commons-logging нарушает модульность приложения, т.к. они оба используют один конфигурационный файл для всех бандлов и packages.
Понравилось сообщение - подпишитесь на блог или читайте меня в twitter
11 комментариев:
а реальное применение какое?
так как логгирование есть и перекомпиленное под осги.
ЗЫ: кстати кто знает быстрое логгирование? и с которым не проседает скорость (если логгирование выключено)?
Ну так это и был конкретный пример, потому что несмотря на то, что существует соответствующий OSGi-бандл, его еще надо настроить - дать ему возможность использовать конфиг.
>кто знает быстрое логгирование?
http://logback.qos.ch/
хорош (от автора log4j),
но работает ли он с OSGi и как я не знаю
Спасибо!
а как использовать в бандле зависимости в виде jar librarys?(те если моему бандлу нужны jar).
thanks!
Вам нужно положить эти jar-ники в отдельный каталог внутри бандла, например lib и перечислить их в параметре Bundle-Classpath манифеста бандла, например так:
Bundle-ClassPath: lib/axis.jar,
lib/commons-discovery-0.2.jar,
lib/commons-logging-1.0.4.jar,
lib/jaxrpc.jar,
lib/saaj.jar,
lib/wsdl4j-1.5.1.jar,
.
Точка в конце обозначает собственные классы бандла.
спасибо, а что делать с config/properties files?
тоже самое
Если они просто лежат в каталогах вашего бандла, то они по-умолчанию попадут в classpath, если же они лежат внутри какого-то jar-а, то перечислите этот jar.
как я могу к своему бандлу подсоеденить сторонний, которий я скачал из репозитория spring
Чтобы ответить на этот вопрос нужно знать что вы имеете ввиду под словом "присоединить".
в своем проекте я использую сторонние библиотеки. с однои из них (c3p0 pool) происходит проблема. я скачал ее как бандл из spring repository и хочу попробовать исползовать подругому(вдруг поможет :( )
Отправить комментарий
Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!