001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.filter;
018:
019: import java.io.IOException;
020:
021: import javax.servlet.Filter;
022: import javax.servlet.FilterChain;
023: import javax.servlet.ServletException;
024: import javax.servlet.ServletRequest;
025: import javax.servlet.ServletResponse;
026:
027: import org.springframework.web.context.WebApplicationContext;
028: import org.springframework.web.context.support.WebApplicationContextUtils;
029:
030: /**
031: * Proxy for a standard Servlet 2.3 Filter, delegating to a Spring-managed
032: * bean that implements the Filter interface. Supports a "targetBeanName"
033: * filter init-param in <code>web.xml</code>, specifying the name of the
034: * target bean in the Spring application context.
035: *
036: * <p><code>web.xml</code> will usually contain a DelegatingFilterProxy definition,
037: * with the specified <code>filter-name</code> corresponding to a bean name in
038: * Spring's root application context. All calls to the filter proxy will then
039: * be delegated to that bean in the Spring context, which is required to implement
040: * the standard Servlet 2.3 Filter interface.
041: *
042: * <p>This approach is particularly useful for Filter implementation with complex
043: * setup needs, allowing to apply the full Spring bean definition machinery to
044: * Filter instances. Alternatively, consider standard Filter setup in combination
045: * with looking up service beans from the Spring root application context.
046: *
047: * <p><b>NOTE:</b> The lifecycle methods defined by the Servlet Filter interface
048: * will by default <i>not</i> be delegated to the target bean, relying on the
049: * Spring application context to manage the lifecycle of that bean. Specifying
050: * the "targetFilterLifecycle" filter init-param as "true" will enforce invocation
051: * of the <code>Filter.init</code> and <code>Filter.destroy</code> lifecycle methods
052: * on the target bean, letting the servlet container manage the filter lifecycle.
053: *
054: * <p>This class is inspired by Acegi Security's FilterToBeanProxy class,
055: * written by Ben Alex.
056: *
057: * @author Juergen Hoeller
058: * @since 1.2
059: * @see #setTargetBeanName
060: * @see #setTargetFilterLifecycle
061: * @see javax.servlet.Filter#doFilter
062: * @see javax.servlet.Filter#init
063: * @see javax.servlet.Filter#destroy
064: */
065: public class DelegatingFilterProxy extends GenericFilterBean {
066:
067: private String targetBeanName;
068:
069: private boolean targetFilterLifecycle = false;
070:
071: private Filter delegate;
072:
073: /**
074: * Set the name of the target bean in the Spring application context.
075: * The target bean must implement the standard Servlet 2.3 Filter interface.
076: * <p>By default, the <code>filter-name</code> as specified for the
077: * DelegatingFilterProxy in <code>web.xml</code> will be used.
078: */
079: public void setTargetBeanName(String targetBeanName) {
080: this .targetBeanName = targetBeanName;
081: }
082:
083: /**
084: * Return the name of the target bean in the Spring application context.
085: */
086: protected String getTargetBeanName() {
087: return targetBeanName;
088: }
089:
090: /**
091: * Set whether to invoke the <code>Filter.init</code> and
092: * <code>Filter.destroy</code> lifecycle methods on the target bean.
093: * <p>Default is "false"; target beans usually rely on the Spring application
094: * context for managing their lifecycle. Setting this flag to "true" means
095: * that the servlet container will control the lifecycle of the target
096: * Filter, with this proxy delegating the corresponding calls.
097: */
098: public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {
099: this .targetFilterLifecycle = targetFilterLifecycle;
100: }
101:
102: /**
103: * Return whether to invoke the <code>Filter.init</code> and
104: * <code>Filter.destroy</code> lifecycle methods on the target bean.
105: */
106: protected boolean isTargetFilterLifecycle() {
107: return targetFilterLifecycle;
108: }
109:
110: protected void initFilterBean() throws ServletException {
111: // If no target bean name specified, use filter name.
112: if (this .targetBeanName == null) {
113: this .targetBeanName = getFilterName();
114: }
115: // Fetch Spring root application context and initialize the delegate early,
116: // if possible. If the root application context will be started after this
117: // filter proxy, we'll have to resort to lazy initialization.
118: WebApplicationContext wac = WebApplicationContextUtils
119: .getWebApplicationContext(getServletContext());
120: if (wac != null) {
121: this .delegate = initDelegate(wac);
122: }
123: }
124:
125: public void doFilter(ServletRequest request,
126: ServletResponse response, FilterChain filterChain)
127: throws ServletException, IOException {
128:
129: // Lazily initialize the delegate if necessary.
130: if (this .delegate == null) {
131: WebApplicationContext wac = WebApplicationContextUtils
132: .getRequiredWebApplicationContext(getServletContext());
133: this .delegate = initDelegate(wac);
134: }
135:
136: // Let the delegate perform the actual doFilter opeation.
137: this .delegate.doFilter(request, response, filterChain);
138: }
139:
140: public void destroy() {
141: if (this .delegate != null) {
142: destroyDelegate(this .delegate);
143: }
144: }
145:
146: /**
147: * Initialize the Filter delegate, defined as bean the given Spring
148: * application context.
149: * <p>Default implementation fetches the bean from the application context
150: * and calls the standard <code>Filter.init</code> method on it, passing
151: * in the FilterConfig of this Filter proxy.
152: * @param wac the root application context
153: * @return the initialized delegate Filter
154: * @throws ServletException if thrown by the Filter
155: * @see #getTargetBeanName()
156: * @see #isTargetFilterLifecycle()
157: * @see #getFilterConfig()
158: * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
159: */
160: protected Filter initDelegate(WebApplicationContext wac)
161: throws ServletException {
162: Filter delegate = (Filter) wac.getBean(getTargetBeanName(),
163: Filter.class);
164: if (isTargetFilterLifecycle()) {
165: delegate.init(getFilterConfig());
166: }
167: return delegate;
168: }
169:
170: /**
171: * Destroy the Filter delegate.
172: * Default implementation simply calls <code>Filter.destroy</code> on it.
173: * @param delegate the Filter delegate (never <code>null</code>)
174: * @see #isTargetFilterLifecycle()
175: * @see javax.servlet.Filter#destroy()
176: */
177: protected void destroyDelegate(Filter delegate) {
178: if (isTargetFilterLifecycle()) {
179: this.delegate.destroy();
180: }
181: }
182:
183: }
|