You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by lh...@apache.org on 2009/09/16 19:23:12 UTC

svn commit: r815896 - in /incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web: config/ filter/mgt/ servlet/

Author: lhazlewood
Date: Wed Sep 16 17:23:12 2009
New Revision: 815896

URL: http://svn.apache.org/viewvc?rev=815896&view=rev
Log:
Filter clean-up - refactored OncePerRequestFilter out to the new AbstractFilter and NamedFilter for clarity and to constrain OPRF to only already-filtered checks (overall functionality remains the same though).  Modified WebConfiguration to add a getFilterChainResolver method and deprecated the old getFilterChain method (it still functions as expected for now).  Further modifications will wait pending mailing list discussion 'Configuration: ShiroFilter vs WebConfiguration'.  Also fixed various JavaDoc/spelling mistakes.

Added:
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AbstractFilter.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/NameableFilter.java
Modified:
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/IniWebConfiguration.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/WebConfiguration.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AdviceFilter.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/OncePerRequestFilter.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ServletContextSupport.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ShiroFilter.java

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/IniWebConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/IniWebConfiguration.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/IniWebConfiguration.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/IniWebConfiguration.java Wed Sep 16 17:23:12 2009
@@ -24,6 +24,7 @@
 import org.apache.shiro.mgt.RealmSecurityManager;
 import org.apache.shiro.util.CollectionUtils;
 import org.apache.shiro.web.DefaultWebSecurityManager;
+import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,6 +57,7 @@
     private PathMatchingFilterChainResolver resolver;
 
     public IniWebConfiguration() {
+        this.resolver = new PathMatchingFilterChainResolver();
     }
 
     /**
@@ -84,6 +86,10 @@
         return resolver.getChain(request, response, originalChain);
     }
 
+    public FilterChainResolver getFilterChainResolver() {
+        return resolver;
+    }
+
     /**
      * Creates a new, uninitialized <code>SecurityManager</code> instance that will be used to build up
      * the Shiro environment for the web application.

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/WebConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/WebConfiguration.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/WebConfiguration.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/config/WebConfiguration.java Wed Sep 16 17:23:12 2009
@@ -21,6 +21,10 @@
 import org.apache.shiro.config.Configuration;
 import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 
+import javax.servlet.FilterChain;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
 /**
  * A {@code WebConfiguration} configures Shiro components in a web-enabled application.
  * <p/>
@@ -30,5 +34,25 @@
  *
  * @since 0.9
  */
-public interface WebConfiguration extends Configuration, FilterChainResolver {
+public interface WebConfiguration extends Configuration {
+
+    /**
+     * Returns the filter chain that should be executed for the given request, or {@code null} if the
+     * original chain should be used.
+     * <p/>
+     * This method allows a implementation to define arbitrary security {@link javax.servlet.Filter Filter}
+     * chains for any given request or URL pattern.
+     *
+     * @param request       the incoming ServletRequest
+     * @param response      the outgoing ServletResponse
+     * @param originalChain the original {@code FilterChain} intercepted by the ShiroFilter.
+     * @return the filter chain that should be executed for the given request, or {@code null} if the
+     *         original chain should be used.
+     * @deprecated The WebConfiguration instance should return an instance of a FilterChainResolver via the
+     *             {@link #getFilterChainResolver()} method instead.
+     */
+    @Deprecated
+    FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);
+
+    FilterChainResolver getFilterChainResolver();
 }

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java Wed Sep 16 17:23:12 2009
@@ -116,7 +116,7 @@
      * The {@code chainDefinition} method argument is expected to conform to the following format:
      * <pre>
      * filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]</pre>
-     * Where
+     * where
      * <ol>
      * <li>{@code filterN} is the name of a filter previously
      * {@link #addFilter(String, javax.servlet.Filter) registered} with the manager, and</li>
@@ -131,7 +131,7 @@
      * <h3>Examples</h3>
      * <pre>/account/** = authcBasic</pre>
      * This example says &quot;Create a filter named '{@code /account/**}' consisting of only the '{@code authcBasic}'
-     * filter.&quot;.  Also because the {@code authcBasic} filter does not need any path-specific
+     * filter&quot;.  Also because the {@code authcBasic} filter does not need any path-specific
      * config, it doesn't have any config brackets {@code []}.
      * <p/>
      * <pre>/remoting/** = authcBasic, roles[b2bClient], perms[&quot;remote:invoke:wan,lan&quot;]</pre>

Added: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AbstractFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AbstractFilter.java?rev=815896&view=auto
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AbstractFilter.java (added)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AbstractFilter.java Wed Sep 16 17:23:12 2009
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.shiro.web.servlet;
+
+import org.apache.shiro.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+
+/**
+ * Base abstract Filter simplifying Filter initialization and {@link #getInitParam(String) access} to init parameters.
+ * Subclass initialization logic should be performed by overriding the {@link #onFilterConfigSet()} template method.
+ * FilterChain execution logic (the
+ * {@link #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} method
+ * is left to subclasses.
+ *
+ * @since 1.0
+ */
+public abstract class AbstractFilter extends ServletContextSupport implements Filter {
+
+    private static transient final Logger log = LoggerFactory.getLogger(AbstractFilter.class);
+
+    /**
+     * FilterConfig provided by the Servlet container at start-up.
+     */
+    protected FilterConfig filterConfig;
+
+    /**
+     * Returns the servlet container specified {@code FilterConfig} instance provided at
+     * {@link #init(javax.servlet.FilterConfig) startup}.
+     *
+     * @return the servlet container specified {@code FilterConfig} instance provided at start-up.
+     */
+    public FilterConfig getFilterConfig() {
+        return filterConfig;
+    }
+
+    /**
+     * Sets the FilterConfig <em>and</em> the {@code ServletContext} as attributes of this class for use by
+     * subclasses.  That is:
+     * <pre>
+     * this.filterConfig = filterConfig;
+     * setServletContext(filterConfig.getServletContext());</pre>
+     *
+     * @param filterConfig the FilterConfig instance provided by the Servlet container at start-up.
+     */
+    public void setFilterConfig(FilterConfig filterConfig) {
+        this.filterConfig = filterConfig;
+        setServletContext(filterConfig.getServletContext());
+    }
+
+    /**
+     * Returns the value for the named {@code init-param}, or {@code null} if there was no {@code init-param}
+     * specified by that name.
+     *
+     * @param paramName the name of the {@code init-param}
+     * @return the value for the named {@code init-param}, or {@code null} if there was no {@code init-param}
+     *         specified by that name.
+     */
+    protected String getInitParam(String paramName) {
+        FilterConfig config = getFilterConfig();
+        return StringUtils.clean(config.getInitParameter(paramName));
+    }
+
+    /**
+     * Sets the filter's {@link #setFilterConfig filterConfig} and then immediately calls
+     * {@link #onFilterConfigSet() onFilterConfigSet()} to trigger any processing a subclass might wish to perform.
+     *
+     * @param filterConfig the servlet container supplied FilterConfig instance.
+     * @throws javax.servlet.ServletException if {@link #onFilterConfigSet() onFilterConfigSet()} throws an Exception.
+     */
+    public final void init(FilterConfig filterConfig) throws ServletException {
+        setFilterConfig(filterConfig);
+        try {
+            onFilterConfigSet();
+        } catch (Exception e) {
+            if (e instanceof ServletException) {
+                throw (ServletException) e;
+            } else {
+                if (log.isErrorEnabled()) {
+                    log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
+                }
+                throw new ServletException(e);
+            }
+        }
+    }
+
+    /**
+     * Template method to be overridden by subclasses to perform initialization logic at start-up.  The
+     * {@code ServletContext} and {@code FilterConfig} will be accessible
+     * (and non-{@code null}) at the time this method is invoked via the
+     * {@link #getServletContext() getServletContext()} and {@link #getFilterConfig() getFilterConfig()}
+     * methods respectively.
+     * <p/>
+     * {@code init-param} values may be conveniently obtained via the {@link #getInitParam(String)} method.
+     *
+     * @throws Exception if the subclass has an error upon initialization.
+     */
+    protected void onFilterConfigSet() throws Exception {
+    }
+
+    /**
+     * Default no-op implementation that can be overridden by subclasses for custom cleanup behavior.
+     */
+    public void destroy() {
+    }
+
+
+}
\ No newline at end of file

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AdviceFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AdviceFilter.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AdviceFilter.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/AdviceFilter.java Wed Sep 16 17:23:12 2009
@@ -28,7 +28,7 @@
 import java.io.IOException;
 
 /**
- * A Servlet Filter that enables AOP-style &quot;around&quot; advice for a SerlvetRequest via
+ * A Servlet Filter that enables AOP-style &quot;around&quot; advice for a ServletRequest via
  * {@link #preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) preHandle},
  * {@link #postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) postHandle},
  * and {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion}
@@ -45,14 +45,14 @@
     private static final Logger log = LoggerFactory.getLogger(AdviceFilter.class);
 
     /**
-     * Returns <code>true</code> if the filter chain should be allowed to continue, <code>false</code> otherwise.
+     * Returns {@code true} if the filter chain should be allowed to continue, {@code false} otherwise.
      * It is called before the chain is actually consulted/executed.
      * <p/>
-     * The default implementation returns <code>true</code> always and exists as a template method for subclasses.
+     * The default implementation returns {@code true} always and exists as a template method for subclasses.
      *
      * @param request  the incoming ServletRequest
      * @param response the outgoing ServletResponse
-     * @return <code>true</code> if the filter chain should be allowed to continue, <code>false</code> otherwise.
+     * @return {@code true} if the filter chain should be allowed to continue, {@code false} otherwise.
      * @throws Exception if there is any error.
      */
     protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
@@ -73,12 +73,13 @@
      * @param response the outgoing ServletResponse
      * @throws Exception if an error occurs.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
     }
 
     /**
-     * Called in all cases in a <code>finally</code> block even if {@link #preHandle preHandle} returns
-     * <code>false</code> or if an exception is thrown during filter chain processing.  Can be used for resource
+     * Called in all cases in a {@code finally} block even if {@link #preHandle preHandle} returns
+     * {@code false} or if an exception is thrown during filter chain processing.  Can be used for resource
      * cleanup if so desired.
      * <p/>
      * The default implementation does nothing (no-op) and exists as a template method for subclasses.
@@ -86,10 +87,11 @@
      * @param request   the incoming ServletRequest
      * @param response  the outgoing ServletResponse
      * @param exception any exception thrown during {@link #preHandle preHandle}, {@link #executeChain executeChain},
-     *                  or {@link #postHandle postHandle} execution, or <code>null</code> if no exception was thrown
+     *                  or {@link #postHandle postHandle} execution, or {@code null} if no exception was thrown
      *                  (i.e. the chain processed successfully).
      * @throws Exception if an error occurs.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
     }
 
@@ -120,7 +122,6 @@
      * @throws ServletException if a servlet-related error occurs
      * @throws IOException      if an IO error occurs
      */
-    @SuppressWarnings({"ThrowFromFinallyBlock"})
     public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
             throws ServletException, IOException {
 
@@ -130,7 +131,7 @@
 
             boolean continueChain = preHandle(request, response);
             if (log.isTraceEnabled()) {
-                log.trace("Invked preHandle method.  Continuing chain?: [" + continueChain + "]");
+                log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");
             }
 
             if (continueChain) {
@@ -150,7 +151,7 @@
     }
 
     /**
-     * Executes cleanup logic in the <code>finally</code> code block in the
+     * Executes cleanup logic in the {@code finally} code block in the
      * {@link #doFilterInternal(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilterInternal}
      * implementation.
      * <p/>
@@ -158,12 +159,12 @@
      * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion}
      * as well as handles any exceptions properly.
      *
-     * @param request  the incoming <code>ServletRequest</code>
-     * @param response the outgoing <code>ServletResponse</code>
-     * @param existing any exception that might have occurred while executing the <code>FilterChain</code> or
-     *                 pre or post advice, or <code>null</code> if the pre/chain/post excution did not throw an <code>Exception</code>.
-     * @throws ServletException if any exception other than an <code>IOException</code> is thrown.
-     * @throws IOException      if the pre/chain/post execution throw an <code>IOException</code>
+     * @param request  the incoming {@code ServletRequest}
+     * @param response the outgoing {@code ServletResponse}
+     * @param existing any exception that might have occurred while executing the {@code FilterChain} or
+     *                 pre or post advice, or {@code null} if the pre/chain/post execution did not throw an {@code Exception}.
+     * @throws ServletException if any exception other than an {@code IOException} is thrown.
+     * @throws IOException      if the pre/chain/post execution throw an {@code IOException}
      */
     protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
             throws ServletException, IOException {
@@ -176,6 +177,9 @@
         } catch (Exception e) {
             if (exception == null) {
                 exception = e;
+            } else {
+                log.debug("afterCompletion implementation threw an exception.  This will be ignored to " +
+                        "allow the original source exception to be propagated.", e);
             }
         }
         if (exception != null) {
@@ -184,10 +188,13 @@
             } else if (exception instanceof IOException) {
                 throw (IOException) exception;
             } else {
-                String msg = "Filter execution resulted in an unexpected Exception " +
-                        "(not IOException or ServletException as the Filter api recommends).  " +
-                        "Wrapping in ServletException and propagating.";
-                throw new ServletException(msg, exception);
+                if (log.isDebugEnabled()) {
+                    String msg = "Filter execution resulted in an unexpected Exception " +
+                            "(not IOException or ServletException as the Filter API recommends).  " +
+                            "Wrapping in ServletException and propagating.";
+                    log.debug(msg);
+                }
+                throw new ServletException(exception);
             }
         }
     }

Added: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/NameableFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/NameableFilter.java?rev=815896&view=auto
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/NameableFilter.java (added)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/NameableFilter.java Wed Sep 16 17:23:12 2009
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.shiro.web.servlet;
+
+import org.apache.shiro.util.Nameable;
+
+import javax.servlet.FilterConfig;
+
+/**
+ * Allows a filter to be named via JavaBeans-compatible
+ * {@link #getName()}/{@link #setName(String)} methods.  If no name is specified, the name of the filter will
+ * default to the name given to it in {@code web.xml} (the {@code FilterConfig}'s
+ * {@link javax.servlet.FilterConfig#getFilterName() filterName}).
+ *
+ * @since 1.0
+ */
+public abstract class NameableFilter extends AbstractFilter implements Nameable {
+
+    /**
+     * The name of this filter, unique within an application.
+     */
+    private String name;
+
+    /**
+     * Returns the filter's name.
+     * <p/>
+     * Unless overridden by calling the {@link #setName(String) setName(String)} method, this value defaults to the
+     * filter name as specified by the servlet container at start-up:
+     * <pre>
+     * this.name = {@link #getFilterConfig() getFilterConfig()}.{@link javax.servlet.FilterConfig#getFilterName() getName()};</pre>
+     *
+     * @return the filter name, or {@code null} if none available
+     * @see javax.servlet.GenericServlet#getServletName()
+     * @see javax.servlet.FilterConfig#getFilterName()
+     */
+    protected String getName() {
+        if (this.name == null) {
+            FilterConfig config = getFilterConfig();
+            if (config != null) {
+                this.name = config.getFilterName();
+            }
+        }
+
+        return this.name;
+    }
+
+    /**
+     * Sets the filter's name.
+     * <p/>
+     * Unless overridden by calling this method, this value defaults to the filter name as specified by the
+     * servlet container at start-up:
+     * <pre>
+     * this.name = {@link #getFilterConfig() getFilterConfig()}.{@link javax.servlet.FilterConfig#getFilterName() getName()};</pre>
+     *
+     * @param name the name of the filter.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns a StringBuilder instance with the {@link #getName() name}, or if the name is {@code null}, just the
+     * {@code super.toStringBuilder()} instance.
+     *
+     * @return a StringBuilder instance to use for appending String data that will eventually be returned from a
+     *         {@code toString()} invocation.
+     */
+    protected StringBuilder toStringBuilder() {
+        String name = getName();
+        if (name == null) {
+            return super.toStringBuilder();
+        } else {
+            StringBuilder sb = new StringBuilder();
+            sb.append(name);
+            return sb;
+        }
+    }
+
+}

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/OncePerRequestFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/OncePerRequestFilter.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/OncePerRequestFilter.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/OncePerRequestFilter.java Wed Sep 16 17:23:12 2009
@@ -18,11 +18,13 @@
  */
 package org.apache.shiro.web.servlet;
 
-import org.apache.shiro.util.Nameable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.servlet.*;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import java.io.IOException;
 
 
@@ -31,18 +33,18 @@
  * on any servlet container. It provides a {@link #doFilterInternal}
  * method with HttpServletRequest and HttpServletResponse arguments.
  * <p/>
- * <p>The {@link #getAlreadyFilteredAttributeName} method determines how
+ * The {@link #getAlreadyFilteredAttributeName} method determines how
  * to identify that a request is already filtered. The default implementation
  * is based on the configured name of the concrete filter instance.
  * <p/>
- * <p><b>NOTE</b> This class was borrowed from the Spring framework, and as such,
+ * <b>NOTE</b> This class was borrowed from the Spring framework, and as such,
  * all copyright notices and author names have remained in tact.
  *
  * @author Les Hazlewood
  * @author Juergen Hoeller
  * @since 0.1
  */
-public abstract class OncePerRequestFilter extends ServletContextSupport implements Filter, Nameable {
+public abstract class OncePerRequestFilter extends NameableFilter {
 
     /**
      * Private internal log instance.
@@ -57,113 +59,7 @@
     public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
 
     /**
-     * FilterConfig provided by the Servlet container at startup.
-     */
-    protected FilterConfig filterConfig;
-
-    /**
-     * The name of this filter, unique within an application.
-     */
-    private String name;
-
-    /**
-     * Returns the servlet container specified <code>FilterConfig</code> instance provided at
-     * {@link #init(javax.servlet.FilterConfig) startup}.
-     *
-     * @return the servlet container specified <code>FilterConfig</code> instance provided at startup.
-     */
-    public FilterConfig getFilterConfig() {
-        return filterConfig;
-    }
-
-    /**
-     * Sets the FilterConfig <em>and</em> the <code>ServletContext</code> as attributes of this class for use by
-     * subclasses.  That is:
-     * <p/>
-     * <code>this.filterConfig = filterConfig;<br/>
-     * setServletContext(filterConfig.getServletContext());</code>
-     *
-     * @param filterConfig the FilterConfig instance provided by the Servlet container at startup.
-     */
-    public void setFilterConfig(FilterConfig filterConfig) {
-        this.filterConfig = filterConfig;
-        setServletContext(filterConfig.getServletContext());
-    }
-
-    /**
-     * Returns the name of this filter.
-     * <p/>
-     * Unless overridden by calling the {@link #setName(String) setName(String)} method, this value defaults to the
-     * filter name as specified by the servlet container at startup:
-     * <p/>
-     * <code>this.name = {@link #getFilterConfig() getFilterConfig()}.{@link FilterConfig#getFilterName() getName()};</code>
-     *
-     * @return the filter name, or <code>null</code> if none available
-     * @see javax.servlet.GenericServlet#getServletName()
-     * @see javax.servlet.FilterConfig#getFilterName()
-     */
-    protected String getName() {
-        if (this.name == null) {
-            FilterConfig config = getFilterConfig();
-            if (config != null) {
-                this.name = config.getFilterName();
-            }
-        }
-
-        return this.name;
-    }
-
-    /**
-     * Sets the filter's name.
-     * <p/>
-     * Unless overridden by calling this method, this value defaults to the filter name as specified by the
-     * servlet container at startup:
-     * <p/>
-     * <code>this.name = {@link #getFilterConfig() getFilterConfig()}.{@link FilterConfig#getFilterName() getName()};</code>
-     *
-     * @param name the name of the filter.
-     */
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    /**
-     * Sets the filter's {@link #setFilterConfig filterConfig} and then immediately calls
-     * {@link #onFilterConfigSet() onFilterConfigSet()} to trigger any processing a subclass might wish to perform.
-     *
-     * @param filterConfig the servlet container supplied FilterConfig instance.
-     * @throws ServletException if {@link #onFilterConfigSet() onFilterConfigSet()} throws an Exception.
-     */
-    public final void init(FilterConfig filterConfig) throws ServletException {
-        setFilterConfig(filterConfig);
-        try {
-            onFilterConfigSet();
-        } catch (Exception e) {
-            if (e instanceof ServletException) {
-                throw (ServletException) e;
-            } else {
-                if (log.isErrorEnabled()) {
-                    log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
-                }
-                throw new ServletException(e);
-            }
-        }
-    }
-
-    /**
-     * Template method to be overridden by subclasses to perform initialization logic at startup.  The
-     * <code>ServletContext</code> and <code>FilterConfig</code> will be accessible
-     * (and non-<code>null</code>) at the time this method is invoked via the
-     * {@link #getServletContext() getServletContext()} and {@link #getFilterConfig() getFilterConfig()}
-     * methods respectively.
-     *
-     * @throws Exception if the subclass has an error upon initialization.
-     */
-    protected void onFilterConfigSet() throws Exception {
-    }
-
-    /**
-     * This <code>doFilter</code> implementation stores a request attribute for
+     * This {@code doFilter} implementation stores a request attribute for
      * "already filtered", proceeding without filtering again if the
      * attribute is already there.
      *
@@ -173,7 +69,6 @@
      */
     public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
             throws ServletException, IOException {
-
         String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
         if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(request)) {
             log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());
@@ -190,7 +85,7 @@
     /**
      * Return name of the request attribute that identifies that a request has already been filtered.
      * <p/>
-     * The default implementation takes the configured {@link #getName() name} and appends ".FILTERED".
+     * The default implementation takes the configured {@link #getName() name} and appends &quot;{@code .FILTERED}&quot;.
      * If the filter is not fully initialized, it falls back to the implementation's class name.
      *
      * @return the name of the request attribute that identifies that a request has already been filtered.
@@ -221,50 +116,16 @@
 
 
     /**
-     * Same contract as for <code>doFilter</code>, but guaranteed to be just invoked once per request.
+     * Same contract as for
+     * {@link #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)},
+     * but guaranteed to be invoked only once per request.
      *
      * @param request  incoming {@code ServletRequest}
      * @param response outgoing {@code ServletResponse}
      * @param chain    the {@code FilterChain} to execute
      * @throws ServletException if there is a problem processing the request
-     * @throws IOException      if there is an IO problem processing the request
+     * @throws IOException      if there is an I/O problem processing the request
      */
     protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
             throws ServletException, IOException;
-
-    /**
-     * Default no-op implementation that can be overridden by subclasses for custom cleanup behavior.
-     */
-    public void destroy() {
-    }
-
-    /**
-     * It is highly recommended not to override this method directly, and instead override the
-     * {@link #toStringBuilder() toStringBuilder()} method, a better-performing alternative.
-     *
-     * @return the String representation of this instance.
-     */
-    @Override
-    public String toString() {
-        return toStringBuilder().toString();
-    }
-
-    /**
-     * Same concept as {@link #toString() toString()}, but returns a {@link StringBuilder} instance instead.
-     * Overriding subclasses would usually call <code>super.toStringBuilder()</code> and use the returned instance
-     * to append to instead of creating a new StringBuilder.
-     *
-     * @return a StringBuilder instance to use for appending String data that will eventually be returned from a
-     *         {@code toString()} invocation.
-     */
-    protected StringBuilder toStringBuilder() {
-        StringBuilder sb = new StringBuilder();
-        String name = getName();
-        if (name == null) {
-            sb.append(super.toString());
-        } else {
-            sb.append(name);
-        }
-        return sb;
-    }
 }

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ServletContextSupport.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ServletContextSupport.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ServletContextSupport.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ServletContextSupport.java Wed Sep 16 17:23:12 2009
@@ -39,11 +39,12 @@
         this.servletContext = servletContext;
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     protected String getContextInitParam(String paramName) {
         return getServletContext().getInitParameter(paramName);
     }
 
-    private ServletContext getServletContextNullCheck() {
+    private ServletContext getRequiredServletContext() {
         ServletContext servletContext = getServletContext();
         if (servletContext == null) {
             String msg = "ServletContext property must be set via the setServletContext method.";
@@ -52,27 +53,42 @@
         return servletContext;
     }
 
-    protected void setAttribute(String key, Object value) {
-        getServletContextNullCheck().setAttribute(key, value);
-    }
-
-    protected Object getAttribute(String key) {
-        return getServletContextNullCheck().getAttribute(key);
-    }
-
-    protected void removeAttribute(String key) {
-        getServletContextNullCheck().removeAttribute(key);
-    }
-
-    protected void bind(String name, String key, Object value) {
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected void setContextAttribute(String key, Object value) {
         if (value == null) {
-            throw new IllegalArgumentException(name + " argument cannot be null.");
-        }
-        if (getAttribute(key) != null) {
-            String msg = name + " already bound to ServletContext.  Please check your configuration to ensure " +
-                    "you don't have mutliple SecurityManager Loaders configured (listener, servlet, etc).";
-            throw new IllegalStateException(msg);
+            removeContextAttribute(key);
+        } else {
+            getRequiredServletContext().setAttribute(key, value);
         }
-        setAttribute(key, value);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected Object getContextAttribute(String key) {
+        return getRequiredServletContext().getAttribute(key);
+    }
+
+    protected void removeContextAttribute(String key) {
+        getRequiredServletContext().removeAttribute(key);
+    }
+
+    /**
+     * It is highly recommended not to override this method directly, and instead override the
+     * {@link #toStringBuilder() toStringBuilder()} method, a better-performing alternative.
+     *
+     * @return the String representation of this instance.
+     */
+    @Override
+    public String toString() {
+        return toStringBuilder().toString();
+    }
+
+    /**
+     * Same concept as {@link #toString() toString()}, but returns a {@link StringBuilder} instance instead.
+     *
+     * @return a StringBuilder instance to use for appending String data that will eventually be returned from a
+     *         {@code toString()} invocation.
+     */
+    protected StringBuilder toStringBuilder() {
+        return new StringBuilder(super.toString());
     }
 }

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ShiroFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ShiroFilter.java?rev=815896&r1=815895&r2=815896&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ShiroFilter.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ShiroFilter.java Wed Sep 16 17:23:12 2009
@@ -19,6 +19,7 @@
 package org.apache.shiro.web.servlet;
 
 import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.config.Configuration;
 import org.apache.shiro.config.ConfigurationException;
 import org.apache.shiro.mgt.SecurityManager;
@@ -26,19 +27,22 @@
 import org.apache.shiro.subject.Subject;
 import org.apache.shiro.util.ClassUtils;
 import org.apache.shiro.util.LifecycleUtils;
-import static org.apache.shiro.util.StringUtils.clean;
 import org.apache.shiro.util.ThreadContext;
 import org.apache.shiro.util.ThreadState;
 import org.apache.shiro.web.DefaultWebSecurityManager;
 import org.apache.shiro.web.WebUtils;
 import org.apache.shiro.web.config.IniWebConfiguration;
 import org.apache.shiro.web.config.WebConfiguration;
+import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 import org.apache.shiro.web.subject.WebSubject;
 import org.apache.shiro.web.subject.support.WebSubjectThreadState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.servlet.*;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.beans.PropertyDescriptor;
@@ -244,6 +248,9 @@
     // Reference to the security manager used by this filter
     protected SecurityManager securityManager;
 
+    // Used to determine which chain should handle an incoming request/response
+    private FilterChainResolver filterChainResolver;
+
     public ShiroFilter() {
         this.configClassName = IniWebConfiguration.class.getName();
     }
@@ -260,28 +267,38 @@
         return securityManager;
     }
 
-    protected void setSecurityManager(org.apache.shiro.mgt.SecurityManager sm) {
+    protected void setSecurityManager(SecurityManager sm) {
         this.securityManager = sm;
     }
 
+    public FilterChainResolver getFilterChainResolver() {
+        return filterChainResolver;
+    }
+
+    public void setFilterChainResolver(FilterChainResolver filterChainResolver) {
+        this.filterChainResolver = filterChainResolver;
+    }
+
     protected void onFilterConfigSet() throws Exception {
         applyInitParams();
         WebConfiguration config = configure();
         setConfiguration(config);
-
-        // Retrieve and store a reference to the security manager
-        SecurityManager securityManager = ensureSecurityManager(config);
-        setSecurityManager(securityManager);
+        ensureSecurityManager(config);
+        applyFilterChainResolver(config);
     }
 
     /**
-     * Retrieves the security manager for the given configuration.
+     * Ensures a SecurityManager exists, and if not, creates one automatically and ensures it is available for
+     * use during requests.
      *
      * @param config the configuration for this filter.
-     * @return the security manager that this filter should use.
      */
-    protected SecurityManager ensureSecurityManager(Configuration config) {
-        SecurityManager securityManager = config.getSecurityManager();
+    protected void ensureSecurityManager(Configuration config) {
+        SecurityManager securityManager = getSecurityManager();
+        boolean existing = securityManager != null;
+        if (!existing && config != null) {
+            securityManager = config.getSecurityManager();
+        }
 
         // If the config doesn't return a security manager, build one by default.
         if (securityManager == null) {
@@ -292,13 +309,24 @@
             securityManager = new DefaultWebSecurityManager();
         }
 
-        return securityManager;
+        if (!existing) {
+            setSecurityManager(securityManager);
+        }
+    }
+
+    protected void applyFilterChainResolver(WebConfiguration config) {
+        FilterChainResolver resolver = getFilterChainResolver();
+        if (resolver == null && config != null) {
+            resolver = config.getFilterChainResolver();
+            if (resolver != null) {
+                setFilterChainResolver(resolver);
+            }
+        }
     }
 
     protected void applyInitParams() {
-        FilterConfig config = getFilterConfig();
 
-        String configCN = clean(config.getInitParameter(CONFIG_CLASS_NAME_INIT_PARAM_NAME));
+        String configCN = getInitParam(CONFIG_CLASS_NAME_INIT_PARAM_NAME);
         if (configCN != null) {
             if (ClassUtils.isAvailable(configCN)) {
                 this.configClassName = configCN;
@@ -310,17 +338,17 @@
             }
         }
 
-        this.config = clean(config.getInitParameter(CONFIG_INIT_PARAM_NAME));
-        this.configUrl = clean(config.getInitParameter(CONFIG_URL_INIT_PARAM_NAME));
+        this.config = getInitParam(CONFIG_INIT_PARAM_NAME);
+        this.configUrl = getInitParam(CONFIG_URL_INIT_PARAM_NAME);
     }
 
     protected WebConfiguration configure() {
-        WebConfiguration conf = (WebConfiguration) ClassUtils.newInstance(this.configClassName);
-        applyFilterConfig(conf);
-        applyUrlConfig(conf);
-        applyEmbeddedConfig(conf);
-        LifecycleUtils.init(conf);
-        return conf;
+        WebConfiguration webConfiguration = (WebConfiguration) ClassUtils.newInstance(this.configClassName);
+        applyFilterConfig(webConfiguration);
+        applyUrlConfig(webConfiguration);
+        applyEmbeddedConfig(webConfiguration);
+        LifecycleUtils.init(webConfiguration);
+        return webConfiguration;
     }
 
     protected void applyFilterConfig(WebConfiguration conf) {
@@ -352,7 +380,7 @@
                     String msg = "The 'config' filter param was specified, but there is no " +
                             "'setConfig(String)' method on the Configuration instance [" + conf + "].  If you do " +
                             "not require the 'config' filter param, please comment it out, or if you do need it, " +
-                            "please ensure your Configuration instance has a 'setConfig(String)' method to receive it.";
+                            "please ensure your Configuration class has a 'setConfig(String)' method to receive it.";
                     throw new ConfigurationException(msg);
                 }
             } catch (Exception e) {
@@ -373,7 +401,7 @@
                     String msg = "The 'configUrl' filter param was specified, but there is no " +
                             "'setConfigUrl(String)' method on the Configuration instance [" + conf + "].  If you do " +
                             "not require the 'configUrl' filter param, please comment it out, or if you do need it, " +
-                            "please ensure your Configuration instance has a 'setConfigUrl(String)' method to receive it.";
+                            "please ensure your Configuration class has a 'setConfigUrl(String)' method to receive it.";
                     throw new ConfigurationException(msg);
                 }
             } catch (Exception e) {
@@ -401,7 +429,7 @@
     }
 
     /**
-     * 'Prepare's the {@code ServletRequest} instance that will be passed to the {@code FilterChain} for request
+     * Prepares the {@code ServletRequest} instance that will be passed to the {@code FilterChain} for request
      * processing.
      * <p/>
      * If the {@code ServletRequest} is an instance of {@link HttpServletRequest}, the value returned from this method
@@ -439,7 +467,7 @@
     }
 
     /**
-     * 'Prepare's the {@code ServletResponse} instance that will be passed to the {@code FilterChain} for request
+     * Prepares the {@code ServletResponse} instance that will be passed to the {@code FilterChain} for request
      * processing.
      * <p/>
      * This implementation delegates to {@link #wrapServletRequest(javax.servlet.http.HttpServletRequest)}
@@ -478,7 +506,7 @@
      * {@link #unbind} method must be called in a {@code finally} block to ensure that the thread remains clean even
      * in the event of an exception thrown while processing the request.  This class's
      * {@link #doFilterInternal(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)}
-     * method implement does indeed perform this.
+     * method implementation does indeed function this way.
      *
      * @param request  the incoming ServletRequest
      * @param response the outgoing ServletResponse
@@ -527,7 +555,7 @@
     @SuppressWarnings({"UnusedDeclaration"})
     protected void updateSessionLastAccessTime(ServletRequest request, ServletResponse response) {
         if (!isHttpSessions()) { //'native' sessions
-            Subject subject = getSecurityManager().getSubject();
+            Subject subject = SecurityUtils.getSubject();
             //Subject should never _ever_ be null, but just in case:
             if (subject != null) {
                 Session session = subject.getSession(false);
@@ -593,13 +621,12 @@
      * <p/>
      * The {@code origChain} argument is the
      * original {@code FilterChain} supplied by the Servlet Container, but it may be modified to provide
-     * more behavior by appending further chains according to the Shiro configuration.
+     * more behavior by pre-pending further chains according to the Shiro configuration.
      * <p/>
      * This implementation returns the chain that will actually be executed by acquiring the chain from a
-     * <code>{@link #getConfiguration() getConfiguration()}.{@link org.apache.shiro.web.config.WebConfiguration#getChain getChain}(request,response,origChain)</code>
-     * method call.  The configuration itself determines which chain to execute, typically based on URL configuration.
-     * If no chain is returned from this method call (returns {@code null}), then the {@code origChain}
-     * will be returned by default.
+     * {@link #getFilterChainResolver() filterChainResolver}.  The resolver determines exactly which chain to
+     * execute, typically based on URL configuration.  If no chain is returned from the resolver call
+     * (returns {@code null}), then the {@code origChain} will be returned by default.
      *
      * @param request   the incoming ServletRequest
      * @param response  the outgoing ServletResponse
@@ -608,13 +635,26 @@
      * @since 1.0
      */
     protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
-        FilterChain chain = getConfiguration().getChain(request, response, origChain);
-        if (chain == null) {
-            chain = origChain;
-            log.trace("No security filter chain configured for the current request.  Using default.");
+        FilterChain chain = origChain;
+        FilterChain resolved = null;
+        FilterChainResolver resolver = getFilterChainResolver();
+        if (resolver != null) {
+            resolved = resolver.getChain(request, response, origChain);
         } else {
-            log.trace(" Using configured filter chain for the current request.");
+            log.trace("No FilterChainResolver configured.  Attempting (deprecated) WebConfiguration resolution.");
+            WebConfiguration config = getConfiguration();
+            if (config != null) {
+                //noinspection deprecation
+                resolved = config.getChain(request, response, origChain);
+            }
         }
+        if (resolved != null) {
+            log.trace("Resolved a configured FilterChain for the current request.");
+            chain = resolved;
+        } else {
+            log.trace("No FilterChain configured for the current request.  Using the default.");
+        }
+
         return chain;
     }
 
@@ -626,7 +666,6 @@
      * to allow the application's Shiro configuration to determine exactly how the chain should execute.  The resulting
      * value from that call is then executed directly by calling the returned {@code FilterChain}'s
      * {@link FilterChain#doFilter doFilter} method.  That is:
-     * <p/>
      * <pre>
      * FilterChain chain = {@link #getExecutionChain}(request, response, origChain);
      * chain.{@link FilterChain#doFilter doFilter}(request,response);</pre>