Separate Spring context for DelegatingFilterProxy
I am attempting to install a filter into a Spring web app using DelegatingFilterProxy. The app is a black box to me. I have no control over it, but I know it uses Spring. I control only my filter.
The app itself is configured in web.xml (tomcat 7) in the usual way, with <listener>...ContextLoaderListener...</listener>
with the Spring config specified via <context-param>
.
My first attempt was to share the app's context. I added my own spring config XML to context-param. My filter loads just fine, but I broke the app. I'm not sure exactly how it's broken, but it looks like it can no longer make database connections. I checked the obvious things. There are no bean name conflicts or property name conflicts.
What I would really like to do is have 2 completely separate contexts so that, presumably, there would be no way for my filter to affect the black box application. Is there a way I can configure web.xml to cause Spring to create a new context just for my filter?
There are several similar questions on stackoverflow, but the details differ considerably.
I'm posting my web.xml as requested, but bear in mind that this is not what I'm trying to do. What you see here is my filter and the web app sharing the same context, which is not what I want. I am asking if it's possible to have two completely separate contexts so that the filter and the web app are completely insulated from each other (at least at the Spring level).
My parts below are the filter declaration and the filter mapping and the second line of spring xml context configuration (classpath:...)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>xxx</display-name>
<description>xxx</description>
<filter>
<filter-name>myAccessFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myAccessFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/applicationContext.xml
classpath:my-access-spring.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
<servlet>
<servlet-name>spring-flex</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>xxx-rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all /messagbroker requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>spring-flex</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>xxx-rest</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<error-page>
<error-code>401</error-code>
<location>/error-401.html</location>
</error-page>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Time passes: I have more information as to why a shared context in this situation is a bad idea. It turns out that I DID have bean conflicts. In particular, both my filter and the underlying application used a bean named "dataSource" (of course).
Once I renamed my bean, I got a very clear message from Spring:
No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: dataSource,deoDataSource
Presumably the app is using wiring by type, not by name, so I have an obvious conflict.
So! The original question still stands: Is it possible to configure Spring to create a separate context just for my filter?
Thanks, Fred
There are two ways how you can approach your problem:
Disable autowiring
Specific beans can be excluded as autowiring candidates via XML config:
<bean class="foo.bar.Baz" autowire-candidate="false" />
Define second application context
I was not correct in my comments to your question - you can not make ContextLoaderListener
load two separate contexts. But you can sort of hack around by using DispatcherServlet
to load second context for you:
<!-- Use this just to load second application context -->
<servlet>
<servlet-name>filterContextLoader</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:filter-context.xml</param-value>
</init-param>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>filterContext</param-value>
</init-param>
</servlet>
<filter>
<filter-name>customFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>filterContext</param-value>
</init-param>
</filter>
Of course there is a downside that DispatcherServlet
will use so called default strategies and automatically register all sorts of unnecessary beans (default handler mappings, handler adapters, ...). But that should be harmless. If you want to register clean context without unnecessary beans, you will need to implement your own ServletContextListener
to do that.