Как получить доступ к контексту приложения Gemini Blueprint из действия Struts1?

В старом добром военном проекте вы просто добавили бы ContextLoaderListener к вашему web.xml, и все бы работало — вы могли бы использовать

WebApplicationContextUtils.getWebApplicationContext(getServlet().getServletContext())

для доступа к контексту приложения из класса Struts 1 Action, например, и весь процесс настройки хорошо документировано. Вы можете найти bean-компоненты в JNDI, если они созданы другими приложениями.

Но что, если я переношу старый добрый архив веб-приложений в пакет веб-приложений и хочу использовать ссылки на службы OSGi вместо JNDI? Вышеупомянутый метод все еще работает, если все, что я хочу, чтобы Spring делал, это управлял bean-компонентами в моем веб-приложении. Я могу создавать экземпляры bean-компонентов и получать их с помощью вышеуказанного служебного метода, и я успешно настроил Gemini Blueprint (ранее Spring DM) для разрешения моих ссылок на службы OSGi.

Проблема в том, что Gemini Blueprint и Spring Struts работают параллельно и, кажется, не знают друг о друге. Контекст, возвращаемый вышеуказанным служебным методом, не содержит bean-компонентов, созданных Gemini Blueprint, таких как импортированные из служб OSGi, и ужасно умирает, если я добавляю ссылку на службу OSGi в стиле Blueprint в конфигурацию XML, проанализированную Spring Struts.

Что мне нужно сделать, чтобы получить доступ к контексту приложения Gemini Blueprint из Struts 1 Action?

Журналы

Некоторые выбранные строки из журналов:

17:12:32,206 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-1) JBAS015876: Starting deployment of "wfadmin-1.0-SNAPSHOT-82a5028.war" (runtime-name: "wfadmin-1.0-SNAPSHOT-82a5028.war")

17:12:36,744 INFO  [io.undertow.servlet] (MSC service thread 1-7) Initializing Spring root WebApplicationContext
17:12:36,745 INFO  [org.springframework.web.context.ContextLoader] (MSC service thread 1-7) Root WebApplicationContext: initialization started
17:12:36,751 INFO  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] (MSC service thread 1-7) Loading XML bean definitions from ServletContext resource [/META-INF/spring/wfadmin-context.xml]
17:12:38,026 FINE  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] (MSC service thread 1-7) Loaded 1 bean definitions from location pattern [/META-INF/spring/wfadmin-context.xml]
17:12:38,026 FINE  [org.springframework.web.context.support.XmlWebApplicationContext] (MSC service thread 1-7) Bean factory for Root WebApplicationContext: org.springframework.beans.factory.support.DefaultListableBeanFactory@440f89d6: defining beans [testBeanInMetaInfSpringWfadmiContextXml]; root of factory hierarchy
17:12:38,027 INFO  [org.springframework.beans.factory.support.DefaultListableBeanFactory] (MSC service thread 1-7) Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@440f89d6: defining beans [testBeanInMetaInfSpringWfadmiContextXml]; root of factory hierarchy
17:12:38,030 FINE  [org.springframework.beans.factory.support.DefaultListableBeanFactory] (MSC service thread 1-7) Finished creating instance of bean 'testBeanInMetaInfSpringWfadmiContextXml'
17:12:38,034 INFO  [org.springframework.web.context.ContextLoader] (MSC service thread 1-7) Root WebApplicationContext: initialization completed in 1289 ms

17:12:38,140 INFO  [org.eclipse.gemini.blueprint.extender.support.DefaultOsgiApplicationContextCreator] (MSC service thread 1-7) Discovered configurations {osgibundle:/META-INF/spring/*.xml} in bundle [Sunstone Workflow Admin (se.sunstone.workflow.web)]
17:12:38,208 FINEST [org.eclipse.gemini.blueprint.io.OsgiBundleResourcePatternResolver] (EclipseGeminiBlueprintExtenderThread-15) Resolved location pattern [osgibundle:/META-INF/spring/*.xml] to resources [URL [bundle://se.sunstone.workflow.web-87-1-0/META-INF/spring/wfadmin-context.xml], URL [bundle://se.sunstone.workflow.web-87-1-0/META-INF/spring/wfadmin-osgi-context.xml]]
17:12:38,258 FINE  [org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext] (EclipseGeminiBlueprintExtenderThread-15) Bean factory for OsgiBundleXmlApplicationContext(bundle=se.sunstone.workflow.web, config=osgibundle:/META-INF/spring/*.xml): org.springframework.beans.factory.support.DefaultListableBeanFactory@22eccb06: defining beans [testBeanInMetaInfSpringWfadmiContextXml,testBeanInMetaInfSpringWfadminOsgiContextXml,wfEngine]; root of factory hierarchy
17:12:38,260 FINEST [org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.MandatoryImporterDependencyFactory] (EclipseGeminiBlueprintExtenderThread-15) Discovered single proxy importers [&wfEngine]
17:12:38,266 FINE  [org.springframework.beans.factory.support.DefaultListableBeanFactory] (EclipseGeminiBlueprintExtenderThread-15) Finished creating instance of bean 'wfEngine'
17:12:38,266 FINEST [org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.MandatoryImporterDependencyFactory] (EclipseGeminiBlueprintExtenderThread-15) Eager importer &wfEngine implies dependecy DependencyService[Name=&wfEngine][Filter=(objectClass=se.sunstone.workflow.WorkflowEngine)][Mandatory=true]
17:12:38,266 FINE  [org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyServiceManager] (EclipseGeminiBlueprintExtenderThread-15) OSGi service dependency for importer [&wfEngine] is already satisfied
17:12:38,266 FINEST [org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyServiceManager] (EclipseGeminiBlueprintExtenderThread-15) Total OSGi service dependencies beans [&wfEngine]
17:12:38,292 FINE  [org.springframework.beans.factory.support.DefaultListableBeanFactory] (EclipseGeminiBlueprintExtenderThread-16) Finished creating instance of bean 'testBeanInMetaInfSpringWfadmiContextXml'
17:12:38,293 FINE  [org.springframework.beans.factory.support.DefaultListableBeanFactory] (EclipseGeminiBlueprintExtenderThread-16) Finished creating instance of bean 'testBeanInMetaInfSpringWfadminOsgiContextXml'

17:12:46,978 FINEST [se.sunstone.util.web.AbstractAction] (default task-1) Beans defined in application context Root WebApplicationContext : 
17:12:46,978 FINEST [se.sunstone.util.web.AbstractAction] (default task-1) testBeanInMetaInfSpringWfadmiContextXml
17:12:46,978 FINEST [se.sunstone.util.web.AbstractAction] (default task-1) End of beans defined in application context Root WebApplicationContext
17:12:46,979 FINEST [org.springframework.beans.factory.support.DefaultListableBeanFactory] (default task-1) No bean named 'wfEngine' found in org.springframework.beans.factory.support.DefaultListableBeanFactory@440f89d6: defining beans [testBeanInMetaInfSpringWfadmiContextXml]; root of factory hierarchy
17:12:46,979 SEVERE [se.sunstone.util.web.AbstractAction] (default task-1) A requested bean does not exist.: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'wfEngine' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:568)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1108)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:278)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1117)
    at se.sunstone.util.web.AbstractAction.getService(AbstractAction.java:53) [AbstractAction.class:]
    ...

В первом разделе (исключая одинокую строку вверху) сообщается, что ContextLoaderListener запускает Spring ContextLoader и обрабатывает META-INF/spring/wfadmin-context.xml, как он настроен.

Во втором разделе сообщается, что Gemini Blueprint определяет, что это пакет Blueprint, и запускает свой собственный контекст из конфигураций META-INF/spring/wfadmin-{,osgi-}context.xml. Мы также видим, что bean-компонент wfEngine успешно импортирован из службы OSGi.

В третьем разделе показано, как мой se.sunstone.util.web.AbstractAction умирает при попытке доступа к bean-компоненту wfEngine в контексте приложения Spring Struts. Это ожидаемо, так как workflow-context.xml содержит только bean-компонент testBeanInMetaInfSpringWorkflowContextXml, но если я включу

<osgi:reference id="wfEngineInMetaInfSpringWfAdminContextXml" interface="se.sunstone.workflow.WorkflowEngine"/>

в workflow-context.xml (с подходящим определением xmlns:osgi) веб-приложение даже не запускается:

09:43:30,026 SEVERE [org.springframework.web.context.ContextLoader] (MSC service thread 1-4) Context initialization failed: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/osgi]
Offending resource: ServletContext resource [/META-INF/spring/wfadmin-context.xml]

Я хотел бы, чтобы был способ сообщить плагину Spring Strugs, чтобы он делился своим контекстом приложения с Gemini Blueprint. Это возможно?

Для полноты конфиги Spring-Blueprint выглядят так:

META-INF/spring/wfadmin-context.xml (Загружается обоими):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

  <bean id="testBeanInMetaInfSpringWfadmiContextXml" class="java.lang.Object"/>
  <!--<osgi:reference id="wfEngineInMetaInfSpringWfAdminContextXml" interface="se.sunstone.workflow.WorkflowEngine"/>-->

</beans>

META-INF/spring/wfadmin-osgi-context.xml (загружено Gemini Blueprint):

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

  <bean id="testBeanInMetaInfSpringWfadminOsgiContextXml" class="java.lang.Object"/>
  <reference id="wfEngine" interface="se.sunstone.workflow.WorkflowEngine" availability="mandatory"/>

</blueprint>

person Emil Lundberg    schedule 04.07.2013    source источник
comment
Я нашел это: static.springsource.org/osgi/docs/ current/reference/html/, который выглядит многообещающе, за исключением того, что речь идет о Spring DM, а не о его преемнике Gemini Blueprint. Кто-нибудь знает, есть ли аналогичная документация и/или банки Gemini Blueprint, соответствующие spring-osgi-web.jar и т. д.?   -  person Emil Lundberg    schedule 04.07.2013
comment
Кажется, я что-то понял: я нашел this, и теперь я пытаюсь заставить все это работать с jar-расширителями Spring DM (pre-Gemini).   -  person Emil Lundberg    schedule 04.07.2013


Ответы (2)


Приложив некоторые усилия, я заставил его работать. ContextLoaderListener по-прежнему актуален, но ему нужно несколько настроек, чтобы сделать его совместимым с OSGi.

Решение в моем случае состояло из нескольких шагов:

Шаг 1. Замените Gemini Blueprint на Spring OSGi

Нам нужно сделать так, чтобы ContextLoaderListener создавало OsgiBundleXmlWebApplicationContext вместо простого XmlWebApplicationContext. Насколько я знаю, еще нет дистрибутива Gemini Blueprint, предоставляющего этот класс, поэтому вместо этого нам нужно будет использовать Spring OSGi, который распространяет пакет spring-osgi-web.

Вместо использования jar-файлов расширения Gemini Blueprint я использовал следующие jar-файлы Spring OSGi:

(и, конечно же, их зависимости, для краткости опущенные)

Поскольку я использовал яркий новый корневой элемент <blueprint> и пространство имен в некоторых конфигурациях контекста моего приложения, их нужно было заменить эквивалентами Spring OSGi (обратите внимание, в частности, как availability="mandatory" теперь становится cardinality="1..1"):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
    <reference id="wfEngine" interface="se.sunstone.workflow.WorkflowEngine" cardinality="1..1"/>
</beans>

В Gemini Blueprint пространства имен взаимозаменяемы, но Spring OSGi предшествует Blueprint, поэтому пространства имен Blueprint и т. д. не работают в Spring OSGi.

Шаг 2. Не позволяйте Spring OSGi распознавать войну как пакет Spring.

Это было достигнуто путем простого перемещения моих контекстов приложений из META-INF/spring и вместо этого размещения их в WEB-INF/applicationContext.xml, который является местом по умолчанию для ContextLoaderListener для поиска конфигурации контекста приложения.

Шаг 3. Предупредите OSGi ContextLoaderListener

Далее я следовал этим инструкциям, чтобы настроить ContextLoaderListener для использования org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext в качестве типа контекста приложения. Затем это встретило меня с новой ошибкой:

java.lang.IllegalArgumentException: bundle context should be set before refreshing the application context

После некоторых поисков я наткнулся на это сообщение в блоге и попробовал это. Поставляемый там OsgiWebBundleContext у меня не работал, я все еще получал ту же ошибку. Добавив некоторый вывод трассировки в этот новый тип контекста, я смог подтвердить, что контекст пакета на самом деле не существует:

17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) Attributes in servletContext:
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.jasper.JSP_PROPERTY_GROUPS
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) javax.servlet.context.tempdir
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.jasper.JSP_TAG_LIBRARIES
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) javax.websocket.server.ServerContainer
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.jasper.SERVLET_VERSION
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.jboss.as.jsf.FACES_ANNOTATIONS
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.tomcat.InstanceManager
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) End of attributes in servletContext

Но, по крайней мере, Spring теперь осведомлён об OSGi, и это начало.

Шаг 4. Обойти отсутствующий BundleContext

Казалось бы, либо BundleContext никогда не устанавливается в ServletContext, либо Spring пытается получить к нему доступ до того, как он будет установлен. В любом случае, этот ответ вдохновил меня на измените класс обходного пути из сообщения в блоге, чтобы использовать FrameworkUtil.getBundle(ClassFromBundle).getBundleContext() для поиска BundleContext:

public class OsgiBundleXmlWebApplicationContextSettingBundleContextFromFrameworkUtil
        extends OsgiBundleXmlWebApplicationContext {
    @Override
    public void setServletContext(ServletContext servletContext) {
        if(getBundleContext() == null) {
            BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
            if(context != null) {
                setBundleContext(context);
            }
        }

        // to call "this.servletContext = servletContext;" in super
        super.setServletContext(servletContext);
    }

}

И это сработало для меня! Возможно, это не самое красивое решение, но пока это единственное, с которым мне удалось работать.*

(Ну, я также заставил его работать, используя BundleActivator, но, вероятно, это было не лучше.)

person Emil Lundberg    schedule 04.07.2013

Заменой spring-dm-web является Gemini Web, и он имеет компонент веб-расширения. У меня это работает, но мне нужно использовать стиль appContext JavaConfig. Почему-то он не обнаруживает пространство имен схемы, когда я настраиваю с помощью XML. Я не пробовал Struts, но пробовал Spring-MVC, и это работает нормально.

Подвох заключается в поиске службы OSGi в реестре служб. Мне пришлось создать активатор и забрать сервис оттуда, создать bean-компонент с помощью @Bean и использовать @Autowired для внедрения этого сервиса в требуемый класс.

Взгляните на этот проект, чтобы увидеть реализацию.

person Aritra    schedule 29.08.2013