如何将 OsgiBundleXmlApplicationContext 设置为 WebApplicationContext 的父级
How to set OsgiBundleXmlApplicationContext as parent of WebApplicationContext
在我的 Camel (2.14.0) 应用程序中,我使用 Spring Web 服务来触发 Camel 路由。该工件构建为 OSGi 包并部署在 Karaf (3.0.2) 中。
对于第一个版本,我将 spring-ws 配置为通过 org.springframework.remoting.support.SimpleHttpServerFactoryBean
使用 JVM 内部 Web 服务器来公开 Web 服务。这很好用。但不是很 OSGi-ish。因此,我想将 org.springframework.ws.transport.http.MessageDispatcherServlet
作为对 Karaf whiteboard extender 的服务发布,如下所示:
<bean id="pas-ws-patient-servlet" class="org.springframework.ws.transport.http.MessageDispatcherServlet">
<property name="contextConfigLocation" value="/endpoint-mapping.xml" />
</bean>
<osgi:service ref="pas-ws-patient-servlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/${pas.ws.patient.contextroot}" />
</service-properties>
</osgi:service>
这对 "regular" servlet 来说就像一个魅力。但是 MessageDispatcherServlet
想要构建自己的 WebApplicationContext
并希望在该上下文中找到类型为 org.springframework.ws.server.EndpointMapping
的 bean。 Camel 提供了 EndpointMapping
的实现,必须与其 spring-ws 组件一起使用。
我面临的问题是端点映射 bean 的同一个实例必须在创建 Camel 上下文的 OsgiBundleXmlApplicationContext
和 MessageDispatcherServlet
创建的应用程序上下文之间共享。如果我的 OsgiBundleXmlApplicationContext
是 WebApplicationContext
的父级,情况就会如此。尽管如何将 WebApplicationContext
的父上下文设置为 "current" 上下文(我从中发布 servlet 作为服务)让我感到困惑。
从 OsgiBundleXmlApplicationContext
中实例化一个 WebApplicationContext
以将其传递给 MessageDispatcherServlet 给我一个例外:
java.lang.IllegalArgumentException: Cannot resolve ServletContextResource without ServletContext
不幸的是,MessageDispatcherServlet
的 WebServiceMessageReceiver
(封装了 EndpointMapping
)是私有成员。所以我不能直接设置映射 bean。
有没有办法创建上下文层次结构?或者可以用另一种方式跨上下文共享 bean 实例吗?
解决方案实际上很简单,并记录在 FrameworkServlet
的 JavaDoc 中,MessageDispatcherServlet
扩展了:
可以在 MessageDispatcherServlet 上设置 ApplicationContextInitializer
。使用上下文初始值设定项将当前应用程序上下文设置为 servlet 应用程序上下文的父级。为此,您还必须实现 ApplicationContextAware
接口以获取当前上下文(在本例中为 OsgiBundleXmlApplicationContext
)。然后将servlet注册为服务:
<!-- This bean sets our current context as the parent context of the XmlWebApplicationContext
that the MessageDispatcherServlet insists on creating. -->
<bean id="springWsContextInitializer" class="x.x.x.SpringWsContextInitializer" />
<bean id="spring-ws-servlet" class="org.springframework.ws.transport.http.MessageDispatcherServlet">
<!-- We inherit all beans from the current context, so no need to specify a separate context file. -->
<property name="contextConfigLocation" value="" />
<property name="ContextInitializers" ref="springWsContextInitializer" />
</bean>
<osgi:service ref="spring-ws-servlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/${servlet.contextroot}" />
<entry key="servlet-name" value="spring-ws-servlet" />
</service-properties>
</osgi:service>
上下文初始值设定项class:
public class SpringWsContextInitializer implements ApplicationContextInitializer<XmlWebApplicationContext>, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void initialize(XmlWebApplicationContext applicationContext) {
applicationContext.setParent(this.applicationContext);
}
}
为了使用 blueprint 而不是 spring-dw 来实现相同的功能,我必须将 SpringWsContextInitializer 更改为这样:
public class SpringWsContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final EndpointMapping endpointMapping;
public SpringWsContextInitializer(final EndpointMapping endpointMapping) {
this.endpointMapping = endpointMapping;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
StaticApplicationContext parentContext = new StaticApplicationContext();
parentContext.refresh();
parentContext.getDefaultListableBeanFactory().registerSingleton("endpointMapping", this.endpointMapping);
applicationContext.setParent(parentContext);
}
}
应该可以将端点映射 bean 发布为 OSGi 服务,然后在 servlet 的上下文文件中引用服务 bean,但是 spring 上需要 OSGi 命名空间的 OSGi 命名空间处理程序无法解析上下文文件。
在我的 Camel (2.14.0) 应用程序中,我使用 Spring Web 服务来触发 Camel 路由。该工件构建为 OSGi 包并部署在 Karaf (3.0.2) 中。
对于第一个版本,我将 spring-ws 配置为通过 org.springframework.remoting.support.SimpleHttpServerFactoryBean
使用 JVM 内部 Web 服务器来公开 Web 服务。这很好用。但不是很 OSGi-ish。因此,我想将 org.springframework.ws.transport.http.MessageDispatcherServlet
作为对 Karaf whiteboard extender 的服务发布,如下所示:
<bean id="pas-ws-patient-servlet" class="org.springframework.ws.transport.http.MessageDispatcherServlet">
<property name="contextConfigLocation" value="/endpoint-mapping.xml" />
</bean>
<osgi:service ref="pas-ws-patient-servlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/${pas.ws.patient.contextroot}" />
</service-properties>
</osgi:service>
这对 "regular" servlet 来说就像一个魅力。但是 MessageDispatcherServlet
想要构建自己的 WebApplicationContext
并希望在该上下文中找到类型为 org.springframework.ws.server.EndpointMapping
的 bean。 Camel 提供了 EndpointMapping
的实现,必须与其 spring-ws 组件一起使用。
我面临的问题是端点映射 bean 的同一个实例必须在创建 Camel 上下文的 OsgiBundleXmlApplicationContext
和 MessageDispatcherServlet
创建的应用程序上下文之间共享。如果我的 OsgiBundleXmlApplicationContext
是 WebApplicationContext
的父级,情况就会如此。尽管如何将 WebApplicationContext
的父上下文设置为 "current" 上下文(我从中发布 servlet 作为服务)让我感到困惑。
从 OsgiBundleXmlApplicationContext
中实例化一个 WebApplicationContext
以将其传递给 MessageDispatcherServlet 给我一个例外:
java.lang.IllegalArgumentException: Cannot resolve ServletContextResource without ServletContext
不幸的是,MessageDispatcherServlet
的 WebServiceMessageReceiver
(封装了 EndpointMapping
)是私有成员。所以我不能直接设置映射 bean。
有没有办法创建上下文层次结构?或者可以用另一种方式跨上下文共享 bean 实例吗?
解决方案实际上很简单,并记录在 FrameworkServlet
的 JavaDoc 中,MessageDispatcherServlet
扩展了:
可以在 MessageDispatcherServlet 上设置 ApplicationContextInitializer
。使用上下文初始值设定项将当前应用程序上下文设置为 servlet 应用程序上下文的父级。为此,您还必须实现 ApplicationContextAware
接口以获取当前上下文(在本例中为 OsgiBundleXmlApplicationContext
)。然后将servlet注册为服务:
<!-- This bean sets our current context as the parent context of the XmlWebApplicationContext
that the MessageDispatcherServlet insists on creating. -->
<bean id="springWsContextInitializer" class="x.x.x.SpringWsContextInitializer" />
<bean id="spring-ws-servlet" class="org.springframework.ws.transport.http.MessageDispatcherServlet">
<!-- We inherit all beans from the current context, so no need to specify a separate context file. -->
<property name="contextConfigLocation" value="" />
<property name="ContextInitializers" ref="springWsContextInitializer" />
</bean>
<osgi:service ref="spring-ws-servlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/${servlet.contextroot}" />
<entry key="servlet-name" value="spring-ws-servlet" />
</service-properties>
</osgi:service>
上下文初始值设定项class:
public class SpringWsContextInitializer implements ApplicationContextInitializer<XmlWebApplicationContext>, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void initialize(XmlWebApplicationContext applicationContext) {
applicationContext.setParent(this.applicationContext);
}
}
为了使用 blueprint 而不是 spring-dw 来实现相同的功能,我必须将 SpringWsContextInitializer 更改为这样:
public class SpringWsContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final EndpointMapping endpointMapping;
public SpringWsContextInitializer(final EndpointMapping endpointMapping) {
this.endpointMapping = endpointMapping;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
StaticApplicationContext parentContext = new StaticApplicationContext();
parentContext.refresh();
parentContext.getDefaultListableBeanFactory().registerSingleton("endpointMapping", this.endpointMapping);
applicationContext.setParent(parentContext);
}
}
应该可以将端点映射 bean 发布为 OSGi 服务,然后在 servlet 的上下文文件中引用服务 bean,但是 spring 上需要 OSGi 命名空间的 OSGi 命名空间处理程序无法解析上下文文件。