Доступ к параметрам контекста сервлета из расширения CDI

Я пытаюсь написать расширение CDI, которому необходимо получить доступ к параметрам контекста, определенным в web.xml, через <context-param>. Я думаю, что есть два способа сделать это:

  • Как-то получить ServletContext и позвонить getInitParameter()
  • Вручную проанализируйте web.xml

К сожалению, мне нужно ServletContext для обоих решений, и получить его не представляется возможным. Проблема здесь в том, что некоторые контейнеры запускают CDI до создания ServletContext. И даже если ServletContext будет доступен до запуска CDI, кажется, нет никакого способа получить к нему доступ из расширения CDI. Я экспериментировал с ServletContextListener, который хранит ServletContext в статическом ThreadLocal. Кажется, это работает нормально, но это приведет к утечке памяти, так как я не могу надежно очистить ThreadLocal.

Еще два комментария, прежде чем вы ответите:

  • Использование какого-либо другого подхода для чтения параметров конфигурации (например, использование JNDI) для меня не вариант, поскольку я пытаюсь написать расширение CDI для интеграции со сторонней структурой.
  • Я осознаю тот факт, что, вероятно, не будет решения этой проблемы, которое на 100% переносимо между средами/контейнерами. Но я был бы счастлив, если бы нашел решение, которое работает в большинстве случаев.

Спасибо! :)


person chkal    schedule 26.08.2011    source источник


Ответы (3)


Я попытался сделать что-то подобное с разделением контекста с компонентами CDI в целом на JBoss 7.1. Хотя у меня это не сработало, я не уверен, что проблемы вызваны текущим состоянием JBoss7.1, так что, возможно, это сработает у вас?

Что я сделал, так это что-то при запуске, которое имеет доступ к ServletContext (в моем случае JAX-RS Application, но, вероятно, прослушиватель или сервлет для вас), доступ к bean-компоненту с областью приложения и установка ServletContext в нем.

Чтобы перейти в мир CDI, я использовал рецепт из следующего URI для создания экземпляра компонента: http://docs.jboss.org/weld/reference/1.1.0.Final/en-US./html/extend.html#d0e4978

Соответствующий код выглядит примерно так:

@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> instanceClass) throws NamingException 
{
    BeanManager beanManager 
        = (BeanManager) InitialContext.doLookup("java:comp/BeanManager");

    AnnotatedType<Object> annotatedType
        = (AnnotatedType<Object>) beanManager.createAnnotatedType(instanceClass);

    InjectionTarget<Object> injectionTarget 
        = beanManager.createInjectionTarget(annotatedType);

    CreationalContext<Object> context
        = beanManager.createCreationalContext(null);

    Object instance = injectionTarget.produce(context);

    injectionTarget.inject(instance, context);
    injectionTarget.postConstruct(instance);

    return (T) instance;
}

который вы могли бы затем установить в bean-компонент, который выглядит так:

package some.package;

import javax.enterprise.context.ApplicationScoped;
import javax.servlet.ServletContext;

/** An application context, initialised on application startup. */
@ApplicationScoped
public class AppContext 
{

    private ServletContext servletContext;

    /** Return the servlet context for the current application. */
    public ServletContext getServletContext() 
    {
        return servletContext;
    }

    public void setServletContext(ServletContext servletContext)
    {
        this.servletContext = servletContext;
    }
}

используя фрагмент, например:

getBean(AppContext.class).setServletContext(servletContext);

в вашем коде запуска. Затем вы должны иметь возможность просто @Inject контекста в любой конструкции CDI, в которой вы хотите его использовать... предполагая, что он запускается после инициализации вашего сервлета или чего-то еще.

Например:

@Inject
private AppContext appContext;

Мне будет любопытно, работает ли это в других ситуациях...

person Phasmal    schedule 23.11.2011

Не уверен, какой контейнер вы используете, но похоже, что в JBoss, по крайней мере, вы можете просто внедрить ServletContext с помощью аннотации. Это не сработает для вас, или я неправильно понимаю природу вашего расширения CDI?

РЕДАКТИРОВАТЬ: ах. Я никогда не использовал реализацию CDI, но можно ли создать ServletContextListener, который генерирует событие CDI с ServletContext в качестве одного из свойств события. Затем вы можете просто прослушать событие в своем расширении и извлечь файл ServletContext.

person Femi    schedule 26.08.2011
comment
Расширение выполняется во время запуска реализации CDI. Так что, к сожалению, этот тип инъекции не будет работать на этом раннем этапе, так как процесс начальной загрузки еще не завершен. - person chkal; 26.08.2011
comment
Нет извините! События не будут работать на этом раннем этапе процедуры запуска CDI. - person chkal; 31.08.2011
comment
Я правильно понимаю: вы хотите получить доступ к ServletContext параметрам ДО создания ServletContext? Нет возможности дождаться запуска событий? - person Femi; 31.08.2011
comment
Расширение CDI выполняется только во время запуска контейнера CDI. Так что ждать не вариант. Проблема в том, что НЕКОТОРЫЕ контейнеры (например, JBoss) загружают CDI до создания ServletContext. В других контейнерах (например, Tomcat) может быть возможность получить пользовательский ServletContextLister для выполнения перед загрузкой CDI, но я не вижу возможности хранить ServletContext где-то, где я могу получить к нему доступ из расширения. Вы видите, что ситуация сложная! :) - person chkal; 03.09.2011

Как вы заметили сами, и Феми прокомментировал, если ServletContext недоступен, вы никак не можете получить от него что-либо (например, параметры инициализации). Чтение файла web.xml возможно, но наверняка это безумие и не будет переносимым, но вы всегда можете попробовать сделать это в своем конкретном развертывании, вы можете получить пример чтения чего-либо из WEB-INF здесь

person Kris    schedule 31.08.2011
comment
Спасибо за ваш комментарий. Я думаю, вы правы в том, что ручной анализ web.xml - единственный вариант. Однако я думаю, что сложно получить InputStream для web.xml без ServletContext. есть идеи как это сделать? - person chkal; 03.09.2011