Как получить доступ к контексту приложения Gemini Blueprint из действия Struts1?
В хорошем старом военном проекте вы бы просто добавили ContextLoaderListener
на ваш web.xml
и все будет в значительной степени работать - вы могли бы использовать
WebApplicationContextUtils.getWebApplicationContext(getServlet().getServletContext())
для доступа к контексту приложения из Struts 1 Action
класс, например, и весь процесс настройки хорошо задокументированы. Вы можете искать бины из JNDI, если они созданы другими приложениями.
Но что, если я перенесу это хорошее старое веб-приложение в пакет веб-приложений и захочу использовать ссылки на службы OSGi вместо JNDI? Описанный выше метод все еще работает, если все, что я хочу, чтобы Spring делал, - это управление бинами в моем веб-приложении. Я могу создавать экземпляры 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
, Мы также видим, что боб wfEngine
успешно импортирован из службы OSGi.
Третий раздел показывает, как мой se.sunstone.util.web.AbstractAction
умирает, когда пытается получить доступ к бину wfEngine
в контексте приложения Spring Struts. Это ожидается, так как workflow-context.xml
содержит только 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
(Загружено Близнецами 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>
2 ответа
С некоторыми усилиями я заставил его работать. ContextLoaderListener
Это все еще путь, но ему нужно несколько настроек, чтобы осознать OSGi.
Решение в моем случае состояло из нескольких шагов:
Шаг 1: Замените Gemini Blueprint на Spring OSGi
Нам нужно сделать ContextLoaderListener
создать OsgiBundleXmlWebApplicationContext
вместо равнины XmlWebApplicationContext
, Насколько я знаю, пока нет никакого дистрибутива Gemini Blueprint, предоставляющего этот класс, поэтому нам нужно будет использовать Spring OSGi, который распространял пакет spring-osgi-web.
Вместо использования банок расширителя Gemini Blueprint я использовал следующие баннеры Spring OSGi:
spring-osgi-core-1.2.1.jar
spring-osgi-extender-1.2.1.jar
spring-osgi-io-1.2.1.jar
spring-osgi-web-1.2.1.jar
(и, конечно, их зависимости, опущены для краткости)
Так как я использовал роскошный новый <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 Spring
Это было достигнуто простым перемещением контекста моего приложения из META-INF/spring
и вместо того, чтобы поместить их в WEB-INF/applicationContext.xml
, который является местоположением по умолчанию для ContextLoaderListener, чтобы искать конфигурацию контекста приложения.
Шаг 3: Сделайте осведомленный о ContextLoaderListener OSGi
Затем я следовал этим инструкциям, чтобы настроить 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
, но это, вероятно, не было красивее.)
Замена для spring-dm-web - это Gemini Web, в которой есть компонент веб-расширителя. Это работает для меня, но мне нужно использовать стиль appContext в стиле JavaConfig. Каким-то образом он не обнаруживает пространство имен проекта, когда я настраиваюсь с использованием XML. Я не пробовал Struts, но пробовал Spring-MVC, и это работает нормально.
Подвох ищет сервис OSGi из реестра сервисов. Мне нужно было создать активатор и забрать оттуда сервис, создать бин с помощью @Bean и использовать @Autowired, чтобы внедрить этот сервис в требуемый класс.
Взгляните на этот проект, чтобы увидеть реализацию.