Песочница →
            @Autowired для сервлетов в OSGi-контейнере
        
                    
            
                
      Вместо введения отправляю читателя к отличной статье Использование Spring в OSGi-контейнере которая и послужила отправной точкой для практического изучения. 
Итак, к делу. Рассмотрим классический вариант — есть бизнес-логика приложения и она как-то
взаимодействует с внешним миром. Используем такую связку:
клиент <-> транспорт <-> приемник <-> сериализатор/десериализатор <-> метод бизнес логики.
Сериализатор напрашивается заменяемым модулем, например сериализация в JSON или сериализация в XML.
Про бизнес-логику далее можно забыть, и сосредоточиться на связке приемника и сериализатора.
В качестве приемника используем сервлет, а для сериализатора, для простоты, используем реализацию следующего интерфейса:
Для связывания компонентов используем SpringFramework. Возвращаясь опять же к ссылке в начале статьи, где инъекция интерфейса строится на аннотациях, сервлет описывается примерно так
Оговорюсь, что это надуманный и не оптимальный пример использования сериализации, но
связывание сериализатора с сервлетом вполне правдоподобно.
Запускалось все на сервере Apache Felix с установленными модулями gemini-blueprint и spring.
Лирическое отступление. Веб-сервер Jetty в поставке с феликсом 4.0.1 идет версией 6.1, а значит о java-ee-6 с аннотацией к сервлету @WebServlet можно забыть. Собирать отдельно Jetty 8 не было желания, а с ходу запустить не удалось.
Итак, к моему удивлению после сборки и установки пакета получил в ответ от Jetty ошибку 404 и свалил все на отсутствие маппинга.
Гугление помогло и для старта сервлета был написан класс активатор:
Проект пересобран, запущен.
Пол дела сделано, сервлет стартует и отзывается, но переменная selializator неутешительно указывает на null.
Стоит заметить, что аннотация @Component на сервлете тоже оказала свое влияние. Забегая вперед скажу, что на самом деле создавалось два экземпляра сервлета. Один создавался по аннотации и даже связывался с сериализатором, а второй честно создавался в активаторе и аннотация @Autowired не работала. Поиск сервиса реализующего ISerializer из активатора не увенчались успехом, т.к. вызов активатора проходил раньше, чем нужный мне сервис регистрировался в системе.
Впереди ждало очередное гугление, которое в конечном итоге и позволило решить проблему.
Если класс описан с аннотацией @Component, то к одному из методов класса можно добавить аннотацию @PostConstruct (на метод существуют ограничения, см. документацию к аннотации). В кратце, такой метод вызывается после всех инъекций зависимостей.
Итак, добавляем метод, который зарегистрирует сервлет в Jetty.
В туториалах по связкам servlet-OSGi написано, что если регистрировать сервлет самостоятельно,
но необходимо при выгрузке модуля и резрегистрировать его самостоятельно:
В моем случае вызов разрегистрации (пытался позвать из метода destroy() сервлета) приводила к
ошибке, которая говорила, что сервис уже не в том статусе, т.е. уже разрегистрирован. Видимо
об этом заботится аннотация @Component. На этом я и успокоился.
В классе-активаторе теперь нет необходимости. В итоге получился сервлет, в который можно заинъектить
необходимую реализацию. И все это без утомительных настроечных XML-файлов.
Надеюсь, что данная статья кому-либо поможет и избавит от долгих поисков информации в нете.
Архив с примером
        
        Итак, к делу. Рассмотрим классический вариант — есть бизнес-логика приложения и она как-то
взаимодействует с внешним миром. Используем такую связку:
клиент <-> транспорт <-> приемник <-> сериализатор/десериализатор <-> метод бизнес логики.
Сериализатор напрашивается заменяемым модулем, например сериализация в JSON или сериализация в XML.
Про бизнес-логику далее можно забыть, и сосредоточиться на связке приемника и сериализатора.
В качестве приемника используем сервлет, а для сериализатора, для простоты, используем реализацию следующего интерфейса:
interface ISerializer {
 String selialize(Object obj);
}
Для связывания компонентов используем SpringFramework. Возвращаясь опять же к ссылке в начале статьи, где инъекция интерфейса строится на аннотациях, сервлет описывается примерно так
@Component
public class TestServlet extends HttpServlet {
 @Autowired
 private ISerializer serializer;
@Override
 protected void doGet(HttpServletRequest req, HttpServletResponse res)
 throws ServletException, IOException {
 // ... десериализация и вызов метода БЛ
 // в ответ получили результат как Object result;
res.setContentType("text/plain");
 PrintWriter out = res.getWriter();
 out.println(serializer.serialize(obj));
 }
}
Оговорюсь, что это надуманный и не оптимальный пример использования сериализации, но
связывание сериализатора с сервлетом вполне правдоподобно.
Запускалось все на сервере Apache Felix с установленными модулями gemini-blueprint и spring.
[INFO] Started jetty 6.1.x at port(s) HTTP:8080
____________________________
Welcome to Apache Felix Gogo
g! lb
START LEVEL 1
 ID|State |Level|Name
 0|Active | 0|System Bundle (4.0.1)
 1|Active | 1|AOP Alliance API (1.0.0)
 2|Active | 1|Apache Log4J (1.2.16)
 3|Active | 1|SLF4J API (1.6.1)
 4|Resolved | 1|SLF4J Log4J Binding (1.6.1)
 5|Active | 1|SLF4J Jakarta Commons Logging Over SLF4J Binding (1.6.1)
 6|Active | 1|gemini-blueprint-core (1.0.0.RELEASE)
 7|Active | 1|gemini-blueprint-extender (1.0.0.RELEASE)
 8|Active | 1|gemini-blueprint-io (1.0.0.RELEASE)
 9|Active | 1|Apache Felix Bundle Repository (1.6.6)
 10|Active | 1|Apache Felix Gogo Command (0.12.0)
 11|Active | 1|Apache Felix Gogo Runtime (0.10.0)
 12|Active | 1|Apache Felix Gogo Shell (0.10.0)
 13|Active | 1|Apache Felix Http Jetty (2.2.0)
 14|Active | 1|Apache Felix Http Whiteboard (2.2.0)
 15|Active | 1|Spring AOP (3.1.0.RC1)
 16|Active | 1|Spring ASM (3.1.0.RC1)
 17|Active | 1|Spring Aspects (3.1.0.RC1)
 18|Active | 1|Spring Beans (3.1.0.RC1)
 19|Active | 1|Spring Context (3.1.0.RC1)
 20|Active | 1|Spring Context Support (3.1.0.RC1)
 21|Active | 1|Spring Core (3.1.0.RC1)
 22|Active | 1|Spring Expression Language (3.1.0.RC1)
Лирическое отступление. Веб-сервер Jetty в поставке с феликсом 4.0.1 идет версией 6.1, а значит о java-ee-6 с аннотацией к сервлету @WebServlet можно забыть. Собирать отдельно Jetty 8 не было желания, а с ходу запустить не удалось.
Итак, к моему удивлению после сборки и установки пакета получил в ответ от Jetty ошибку 404 и свалил все на отсутствие маппинга.
Гугление помогло и для старта сервлета был написан класс активатор:
public class Activator implements BundleActivator {
 private ServiceRegistration reg;
@Override
 public void start(BundleContext context) throws Exception {
 Dictionary<String, String> props = new Hashtable<>();
 props.put("alias", "/");
 this.reg = context.registerService(Servlet.class.getName(), new TestServlet(), props);
 }
@Override
 public void stop(BundleContext context) throws Exception {
 this.reg.unregister();
 }
}
Проект пересобран, запущен.
Пол дела сделано, сервлет стартует и отзывается, но переменная selializator неутешительно указывает на null.
Стоит заметить, что аннотация @Component на сервлете тоже оказала свое влияние. Забегая вперед скажу, что на самом деле создавалось два экземпляра сервлета. Один создавался по аннотации и даже связывался с сериализатором, а второй честно создавался в активаторе и аннотация @Autowired не работала. Поиск сервиса реализующего ISerializer из активатора не увенчались успехом, т.к. вызов активатора проходил раньше, чем нужный мне сервис регистрировался в системе.
Впереди ждало очередное гугление, которое в конечном итоге и позволило решить проблему.
Если класс описан с аннотацией @Component, то к одному из методов класса можно добавить аннотацию @PostConstruct (на метод существуют ограничения, см. документацию к аннотации). В кратце, такой метод вызывается после всех инъекций зависимостей.
Итак, добавляем метод, который зарегистрирует сервлет в Jetty.
 @PostConstruct
 public void postConstruct() {
 BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
 Dictionary props = new Properties();
 props.put("alias", "/");
 context.registerService(Servlet.class.getName(), this, props);
 }
В туториалах по связкам servlet-OSGi написано, что если регистрировать сервлет самостоятельно,
но необходимо при выгрузке модуля и резрегистрировать его самостоятельно:
 // регистрация
 ServiceRegistration reg = context.registerService(Servlet.class.getName(), this, props);
// разрегистирация
 reg.unregister();
В моем случае вызов разрегистрации (пытался позвать из метода destroy() сервлета) приводила к
ошибке, которая говорила, что сервис уже не в том статусе, т.е. уже разрегистрирован. Видимо
об этом заботится аннотация @Component. На этом я и успокоился.
В классе-активаторе теперь нет необходимости. В итоге получился сервлет, в который можно заинъектить
необходимую реализацию. И все это без утомительных настроечных XML-файлов.
Надеюсь, что данная статья кому-либо поможет и избавит от долгих поисков информации в нете.
Дополнительные источники
Архив с примером
    
      15.11.2011 20:00+0400
    
        
            
            
            
        