Open Source Repository

Home /spring/spring-web-3.0.5 | Repository Home



org/springframework/web/filter/DelegatingFilterProxy.java
/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Proxy for a standard Servlet 2.3 Filter, delegating to a Spring-managed
 * bean that implements the Filter interface. Supports a "targetBeanName"
 * filter init-param in <code>web.xml</code>, specifying the name of the
 * target bean in the Spring application context.
 *
 <p><code>web.xml</code> will usually contain a DelegatingFilterProxy definition,
 * with the specified <code>filter-name</code> corresponding to a bean name in
 * Spring's root application context. All calls to the filter proxy will then
 * be delegated to that bean in the Spring context, which is required to implement
 * the standard Servlet 2.3 Filter interface.
 *
 <p>This approach is particularly useful for Filter implementation with complex
 * setup needs, allowing to apply the full Spring bean definition machinery to
 * Filter instances. Alternatively, consider standard Filter setup in combination
 * with looking up service beans from the Spring root application context.
 *
 <p><b>NOTE:</b> The lifecycle methods defined by the Servlet Filter interface
 * will by default <i>not</i> be delegated to the target bean, relying on the
 * Spring application context to manage the lifecycle of that bean. Specifying
 * the "targetFilterLifecycle" filter init-param as "true" will enforce invocation
 * of the <code>Filter.init</code> and <code>Filter.destroy</code> lifecycle methods
 * on the target bean, letting the servlet container manage the filter lifecycle.
 *
 <p>This class is inspired by Acegi Security's FilterToBeanProxy class,
 * written by Ben Alex.
 *
 @author Juergen Hoeller
 @author Sam Brannen
 @since 1.2
 @see #setTargetBeanName
 @see #setTargetFilterLifecycle
 @see javax.servlet.Filter#doFilter
 @see javax.servlet.Filter#init
 @see javax.servlet.Filter#destroy
 */
public class DelegatingFilterProxy extends GenericFilterBean {

  private String contextAttribute;

  private String targetBeanName;

  private boolean targetFilterLifecycle = false;

  private Filter delegate;

  private final Object delegateMonitor = new Object();


  /**
   * Set the name of the ServletContext attribute which should be used to retrieve the
   {@link WebApplicationContext} from which to load the delegate {@link Filter} bean.
   */
  public void setContextAttribute(String contextAttribute) {
    this.contextAttribute = contextAttribute;
  }

  /**
   * Return the name of the ServletContext attribute which should be used to retrieve the
   {@link WebApplicationContext} from which to load the delegate {@link Filter} bean.
   */
  public String getContextAttribute() {
    return this.contextAttribute;
  }

  /**
   * Set the name of the target bean in the Spring application context.
   * The target bean must implement the standard Servlet 2.3 Filter interface.
   <p>By default, the <code>filter-name</code> as specified for the
   * DelegatingFilterProxy in <code>web.xml</code> will be used.
   */
  public void setTargetBeanName(String targetBeanName) {
    this.targetBeanName = targetBeanName;
  }

  /**
   * Return the name of the target bean in the Spring application context.
   */
  protected String getTargetBeanName() {
    return this.targetBeanName;
  }

  /**
   * Set whether to invoke the <code>Filter.init</code> and
   <code>Filter.destroy</code> lifecycle methods on the target bean.
   <p>Default is "false"; target beans usually rely on the Spring application
   * context for managing their lifecycle. Setting this flag to "true" means
   * that the servlet container will control the lifecycle of the target
   * Filter, with this proxy delegating the corresponding calls.
   */
  public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {
    this.targetFilterLifecycle = targetFilterLifecycle;
  }

  /**
   * Return whether to invoke the <code>Filter.init</code> and
   <code>Filter.destroy</code> lifecycle methods on the target bean.
   */
  protected boolean isTargetFilterLifecycle() {
    return this.targetFilterLifecycle;
  }


  @Override
  protected void initFilterBean() throws ServletException {
    // If no target bean name specified, use filter name.
    if (this.targetBeanName == null) {
      this.targetBeanName = getFilterName();
    }

    // Fetch Spring root application context and initialize the delegate early,
    // if possible. If the root application context will be started after this
    // filter proxy, we'll have to resort to lazy initialization.
    synchronized (this.delegateMonitor) {
      WebApplicationContext wac = findWebApplicationContext();
      if (wac != null) {
        this.delegate = initDelegate(wac);
      }
    }
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

    // Lazily initialize the delegate if necessary.
    Filter delegateToUse = null;
    synchronized (this.delegateMonitor) {
      if (this.delegate == null) {
        WebApplicationContext wac = findWebApplicationContext();
        if (wac == null) {
          throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
        }
        this.delegate = initDelegate(wac);
      }
      delegateToUse = this.delegate;
    }

    // Let the delegate perform the actual doFilter operation.
    invokeDelegate(delegateToUse, request, response, filterChain);
  }

  @Override
  public void destroy() {
    Filter delegateToUse = null;
    synchronized (this.delegateMonitor) {
      delegateToUse = this.delegate;
    }
    if (delegateToUse != null) {
      destroyDelegate(delegateToUse);
    }
  }


  /**
   * Retrieve a <code>WebApplicationContext</code> from the <code>ServletContext</code>
   * attribute with the {@link #setContextAttribute configured name}. The
   <code>WebApplicationContext</code> must have already been loaded and stored in the
   <code>ServletContext</code> before this filter gets initialized (or invoked).
   <p>Subclasses may override this method to provide a different
   <code>WebApplicationContext</code> retrieval strategy.
   @return the WebApplicationContext for this proxy, or <code>null</code> if not found
   @see #getContextAttribute()
   */
  protected WebApplicationContext findWebApplicationContext() {
    String attrName = getContextAttribute();
    if (attrName != null) {
      return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
    }
    else {
      return WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    }
  }

  /**
   * Initialize the Filter delegate, defined as bean the given Spring
   * application context.
   <p>The default implementation fetches the bean from the application context
   * and calls the standard <code>Filter.init</code> method on it, passing
   * in the FilterConfig of this Filter proxy.
   @param wac the root application context
   @return the initialized delegate Filter
   @throws ServletException if thrown by the Filter
   @see #getTargetBeanName()
   @see #isTargetFilterLifecycle()
   @see #getFilterConfig()
   @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
   */
  protected Filter initDelegate(WebApplicationContext wacthrows ServletException {
    Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
    if (isTargetFilterLifecycle()) {
      delegate.init(getFilterConfig());
    }
    return delegate;
  }

  /**
   * Actually invoke the delegate Filter with the given request and response.
   @param delegate the delegate Filter
   @param request the current HTTP request
   @param response the current HTTP response
   @param filterChain the current FilterChain
   @throws ServletException if thrown by the Filter
   @throws IOException if thrown by the Filter
   */
  protected void invokeDelegate(
      Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

    delegate.doFilter(request, response, filterChain);
  }

  /**
   * Destroy the Filter delegate.
   * Default implementation simply calls <code>Filter.destroy</code> on it.
   @param delegate the Filter delegate (never <code>null</code>)
   @see #isTargetFilterLifecycle()
   @see javax.servlet.Filter#destroy()
   */
  protected void destroyDelegate(Filter delegate) {
    if (isTargetFilterLifecycle()) {
      delegate.destroy();
    }
  }

}