1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.acegisecurity.util;
16
17 import org.springframework.beans.factory.BeanFactoryUtils;
18
19 import org.springframework.context.ApplicationContext;
20
21 import org.springframework.web.context.support.WebApplicationContextUtils;
22
23 import java.io.IOException;
24
25 import java.util.Map;
26
27 import javax.servlet.Filter;
28 import javax.servlet.FilterChain;
29 import javax.servlet.FilterConfig;
30 import javax.servlet.ServletException;
31 import javax.servlet.ServletRequest;
32 import javax.servlet.ServletResponse;
33
34
35 /***
36 * Delegates <code>Filter</code> requests to a Spring-managed bean.
37 *
38 * <p>
39 * This class acts as a proxy on behalf of a target <code>Filter</code> that is
40 * defined in the Spring bean context. It is necessary to specify which target
41 * <code>Filter</code> should be proxied as a filter initialization parameter.
42 * </p>
43 *
44 * <p>
45 * On filter initialisation, the class will use Spring's {@link
46 * WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
47 * method to obtain an <code>ApplicationContext</code> instance. It will
48 * expect to find the target <code>Filter</code> in this
49 * <code>ApplicationContext</code>.
50 * </p>
51 *
52 * <p>
53 * To use this filter, it is necessary to specify <b>one</b> of the following
54 * filter initialization parameters:
55 * </p>
56 *
57 * <ul>
58 * <li>
59 * <code>targetClass</code> indicates the class of the target
60 * <code>Filter</code> defined in the bean context. The only requirements are
61 * that this target class implements the <code>javax.servlet.Filter</code>
62 * interface and at least one instance is available in the
63 * <code>ApplicationContext</code>.
64 * </li>
65 * <li>
66 * <code>targetBean</code> indicates the bean name of the target class.
67 * </li>
68 * </ul>
69 *
70 * If both initialization parameters are specified, <code>targetBean</code>
71 * takes priority.
72 *
73 * <P>
74 * An additional initialization parameter, <code>init</code>, is also
75 * supported. If set to "<code>lazy</code>" the initialization will take place
76 * on the first HTTP request, rather than at filter creation time. This makes
77 * it possible to use <code>FilterToBeanProxy</code> with the Spring
78 * <code>ContextLoaderServlet</code>. Where possible you should not use this
79 * initialization parameter, instead using <code>ContextLoaderListener</code>.
80 * </p>
81 *
82 * <p>
83 * A final optional initialization parameter, <code>lifecycle</code>,
84 * determines whether the servlet container or the IoC container manages the
85 * lifecycle of the proxied filter. When possible you should write your
86 * filters to be managed via the IoC container interfaces such as {@link
87 * org.springframework.beans.factory.InitializingBean} and {@link
88 * org.springframework.beans.factory.DisposableBean}. If you cannot control
89 * the filters you wish to proxy (eg you do not have their source code) you
90 * might need to allow the servlet container to manage lifecycle via the
91 * {@link javax.servlet.Filter#init(javax.servlet.FilterConfig)} and {@link
92 * javax.servlet.Filter#destroy()} methods. If this case, set the
93 * <code>lifecycle</code> initialization parameter to
94 * <code>servlet-container-managed</code>. If the parameter is any other
95 * value, servlet container lifecycle methods will not be delegated through to
96 * the proxy.
97 * </p>
98 *
99 * @author Ben Alex
100 * @version $Id: FilterToBeanProxy.java,v 1.9 2005/11/17 00:56:09 benalex Exp $
101 */
102 public class FilterToBeanProxy implements Filter {
103 private Filter delegate;
104 private FilterConfig filterConfig;
105 private boolean initialized = false;
106 private boolean servletContainerManaged = false;
107
108 public void destroy() {
109 if ((delegate != null) && servletContainerManaged) {
110 delegate.destroy();
111 }
112 }
113
114 public void doFilter(ServletRequest request, ServletResponse response,
115 FilterChain chain) throws IOException, ServletException {
116 if (!initialized) {
117 doInit();
118 }
119
120 delegate.doFilter(request, response, chain);
121 }
122
123 public void init(FilterConfig filterConfig) throws ServletException {
124 this.filterConfig = filterConfig;
125
126 String strategy = filterConfig.getInitParameter("init");
127
128 if ((strategy != null) && strategy.toLowerCase().equals("lazy")) {
129 return;
130 }
131
132 doInit();
133 }
134
135 /***
136 * Allows test cases to override where application context obtained from.
137 *
138 * @param filterConfig which can be used to find the
139 * <code>ServletContext</code>
140 *
141 * @return the Spring application context
142 */
143 protected ApplicationContext getContext(FilterConfig filterConfig) {
144 return WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
145 }
146
147 private synchronized void doInit() throws ServletException {
148 if (initialized) {
149
150 return;
151 }
152
153 String targetBean = filterConfig.getInitParameter("targetBean");
154
155 if ("".equals(targetBean)) {
156 targetBean = null;
157 }
158
159 String lifecycle = filterConfig.getInitParameter("lifecycle");
160
161 if ("servlet-container-managed".equals(lifecycle)) {
162 servletContainerManaged = true;
163 }
164
165 ApplicationContext ctx = this.getContext(filterConfig);
166
167 String beanName = null;
168
169 if ((targetBean != null) && ctx.containsBean(targetBean)) {
170 beanName = targetBean;
171 } else if (targetBean != null) {
172 throw new ServletException("targetBean '" + targetBean +
173 "' not found in context");
174 } else {
175 String targetClassString = filterConfig.getInitParameter(
176 "targetClass");
177
178 if ((targetClassString == null) || "".equals(targetClassString)) {
179 throw new ServletException(
180 "targetClass or targetBean must be specified");
181 }
182
183 Class targetClass;
184
185 try {
186 targetClass = Thread.currentThread().getContextClassLoader()
187 .loadClass(targetClassString);
188 } catch (ClassNotFoundException ex) {
189 throw new ServletException("Class of type " +
190 targetClassString + " not found in classloader");
191 }
192
193 Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx,
194 targetClass, true, true);
195
196 if (beans.size() == 0) {
197 throw new ServletException(
198 "Bean context must contain at least one bean of type " +
199 targetClassString);
200 }
201
202 beanName = (String) beans.keySet().iterator().next();
203 }
204
205 Object object = ctx.getBean(beanName);
206
207 if (!(object instanceof Filter)) {
208 throw new ServletException("Bean '" + beanName +
209 "' does not implement javax.servlet.Filter");
210 }
211
212 delegate = (Filter) object;
213
214 if (servletContainerManaged) {
215 delegate.init(filterConfig);
216 }
217
218
219
220
221 initialized = true;
222 }
223 }