Яндекс.Метрика

    Песочница

    @Autowired для сервлетов в OSGi-контейнере

    Вместо введения отправляю читателя к отличной статье Использование Spring в OSGi-контейнере которая и послужила отправной точкой для практического изучения.
    Итак, к делу. Рассмотрим классический вариант — есть бизнес-логика приложения и она как-то
    взаимодействует с внешним миром. Используем такую связку:
    клиент <-> транспорт <-> приемник <-> сериализатор/десериализатор <-> метод бизнес логики.
    Сериализатор напрашивается заменяемым модулем, например сериализация в 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-файлов.
    Надеюсь, что данная статья кому-либо поможет и избавит от долгих поисков информации в нете.

    Дополнительные источники


    Архив с примером