shiro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lhazlew...@apache.org
Subject svn commit: r814833 - in /incubator/shiro/trunk: core/src/main/java/org/apache/shiro/config/ core/src/test/java/org/apache/shiro/util/ support/spring/src/main/java/org/apache/shiro/spring/ web/src/main/java/org/apache/shiro/web/config/ web/src/main/jav...
Date Mon, 14 Sep 2009 20:33:54 GMT
Author: lhazlewood
Date: Mon Sep 14 20:33:51 2009
New Revision: 814833

URL: http://svn.apache.org/viewvc?rev=814833&view=rev
Log:
Moved Filter management components to new 'mgt' package to cleanly distinguish between frequently-accessed end-user filter support classes and those for framework development.  Refactored IniWebConfiguration to use the new components, utilizing a cleaner 'composition over inheritance' implementation strategy.   Cleaned up various JavaDoc.

Added:
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
      - copied, changed from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/DefaultFilterChainManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
      - copied, changed from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainResolver.java
      - copied, changed from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainResolver.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/NamedFilterList.java
      - copied, changed from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/NamedFilterList.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolver.java
      - copied, changed from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathMatchingFilterChainResolver.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/SimpleNamedFilterList.java
      - copied, changed from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/SimpleNamedFilterList.java
Removed:
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/DefaultFilterChainManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainManager.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainResolver.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/NamedFilterList.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathMatchingFilterChainResolver.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/SimpleNamedFilterList.java
Modified:
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/TextConfiguration.java
    incubator/shiro/trunk/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java
    incubator/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/SpringIniWebConfiguration.java
    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/PathConfigProcessor.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authc/FormAuthenticationFilter.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/PortFilter.java
    incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/SslFilter.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/ProxiedFilterChain.java
    incubator/shiro/trunk/web/src/test/java/org/apache/shiro/web/servlet/ShiroFilterTest.java

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/ReflectionBuilder.java Mon Sep 14 20:33:51 2009
@@ -61,6 +61,7 @@
         setObjects(defaults);
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public Map getObjects() {
         return objects;
     }
@@ -191,7 +192,7 @@
         if (o == null) {
             String msg = "The object with id [" + id + "] has not yet been defined and therefore cannot be " +
                     "referenced.  Please ensure objects are defined in the order in which they should be " +
-                    "created and made avaialable for future reference.";
+                    "created and made available for future reference.";
             throw new UnresolveableReferenceException(msg);
         }
         return o;

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/TextConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/TextConfiguration.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/TextConfiguration.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/TextConfiguration.java Mon Sep 14 20:33:51 2009
@@ -18,17 +18,16 @@
  */
 package org.apache.shiro.config;
 
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.Scanner;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.shiro.ShiroException;
 import org.apache.shiro.io.ResourceException;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.util.Initializable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Scanner;
 
 
 /**
@@ -50,6 +49,7 @@
         return config;
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public void setConfig(String config) {
         this.config = config;
     }
@@ -74,8 +74,7 @@
         }
     }
 
-    public void init() throws ShiroException
-    {
+    public void init() throws ShiroException {
         SecurityManager securityManager = getSecurityManager();
         if (securityManager == null) {
             String config = getConfig();

Modified: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java (original)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java Mon Sep 14 20:33:51 2009
@@ -98,6 +98,18 @@
     }
 
     @Test
+    public void splitTestWithQuotedCommas() {
+        String line = "authc, test[blah], test[\"1,2,3\"], test[]";
+        String[] split = StringUtils.split(line);
+        assertNotNull(split);
+        assertTrue(split.length == 4);
+        assertEquals("authc", split[0]);
+        assertEquals("test[blah]", split[1]);
+        assertEquals("test[1,2,3]", split[2]);
+        assertEquals("test[]", split[3]);
+    }
+
+    @Test
     public void splitWithQuotedCommasAndSpacesAndEscapedQuotes() {
         String line = "shall, \"\"\"we, play\", a, \"\"\"game?";
         String[] split = StringUtils.split(line);

Modified: incubator/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/SpringIniWebConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/SpringIniWebConfiguration.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/SpringIniWebConfiguration.java (original)
+++ incubator/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/SpringIniWebConfiguration.java Mon Sep 14 20:33:51 2009
@@ -75,6 +75,9 @@
 
     protected String securityManagerBeanName;
 
+    public SpringIniWebConfiguration() {
+    }
+
     public String getSecurityManagerBeanName() {
         return securityManagerBeanName;
     }
@@ -83,12 +86,8 @@
         this.securityManagerBeanName = securityManagerBeanName;
     }
 
-    public SpringIniWebConfiguration() {
-    }
-
     @Override
-    public void init() throws ShiroException
-    {
+    public void init() throws ShiroException {
         String beanName = getFilterConfig().getInitParameter(SECURITY_MANAGER_BEAN_NAME_PARAM_NAME);
         if (beanName != null) {
             setSecurityManagerBeanName(beanName);

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=814833&r1=814832&r2=814833&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 Mon Sep 14 20:33:51 2009
@@ -18,42 +18,21 @@
  */
 package org.apache.shiro.web.config;
 
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.shiro.config.ConfigurationException;
 import org.apache.shiro.config.IniConfiguration;
 import org.apache.shiro.config.ReflectionBuilder;
 import org.apache.shiro.mgt.RealmSecurityManager;
-import org.apache.shiro.util.AntPathMatcher;
-import org.apache.shiro.util.PatternMatcher;
-import static org.apache.shiro.util.StringUtils.split;
+import org.apache.shiro.util.CollectionUtils;
 import org.apache.shiro.web.DefaultWebSecurityManager;
-import org.apache.shiro.web.WebUtils;
-import org.apache.shiro.web.filter.PathConfigProcessor;
-import org.apache.shiro.web.filter.authc.AnonymousFilter;
-import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
-import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
-import org.apache.shiro.web.filter.authc.UserFilter;
-import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
-import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
-import org.apache.shiro.web.filter.authz.PortFilter;
-import org.apache.shiro.web.filter.authz.SslFilter;
-import org.apache.shiro.web.servlet.AdviceFilter;
-import org.apache.shiro.web.servlet.ProxiedFilterChain;
+import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.*;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
 
 
 /**
@@ -74,38 +53,9 @@
 
     protected FilterConfig filterConfig;
 
-    protected Map<String, List<Filter>> chains;
-
-    protected PatternMatcher pathMatcher = new AntPathMatcher();
+    private PathMatchingFilterChainResolver resolver;
 
     public IniWebConfiguration() {
-        chains = new LinkedHashMap<String, List<Filter>>();
-    }
-
-    /**
-     * Returns the <code>PatternMatcher</code> used when determining if an incoming request's path
-     * matches a configured filter chain path in the <code>[urls]</code> section.  Unless overridden, the
-     * default implementation is an {@link org.apache.shiro.util.AntPathMatcher AntPathMatcher}.
-     *
-     * @return the <code>PatternMatcher</code> used when determining if an incoming request's path
-     *         matches a configured filter chain path in the <code>[urls]</code> section.
-     * @since 0.9.0
-     */
-    public PatternMatcher getPathMatcher() {
-        return pathMatcher;
-    }
-
-    /**
-     * Sets the <code>PatternMatcher</code> used when determining if an incoming request's path
-     * matches a configured filter chain path in the <code>[urls]</code> section.  Unless overridden, the
-     * default implementation is an {@link org.apache.shiro.util.AntPathMatcher AntPathMatcher}.
-     *
-     * @param pathMatcher the <code>PatternMatcher</code> used when determining if an incoming request's path
-     *                    matches a configured filter chain path in the <code>[urls]</code> section.
-     * @since 0.9.0
-     */
-    public void setPathMatcher(PatternMatcher pathMatcher) {
-        this.pathMatcher = pathMatcher;
     }
 
     /**
@@ -122,103 +72,16 @@
      *
      * @param filterConfig the <code>FilterConfig</code> provided by the Servlet container at webapp startup.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
+    //called via Reflection - DO NOT REMOVE
     public void setFilterConfig(FilterConfig filterConfig) {
         this.filterConfig = filterConfig;
+        this.resolver = new PathMatchingFilterChainResolver(filterConfig);
     }
 
     //TODO - JAVADOC
     public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
-        if (this.chains == null || this.chains.isEmpty()) {
-            return null;
-        }
-
-        String requestURI = getPathWithinApplication(request);
-
-        for (String path : this.chains.keySet()) {
-
-            // If the path does match, then pass on to the subclass implementation for specific checks:
-            if (pathMatches(path, requestURI)) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Matched path [" + path + "] for requestURI [" + requestURI + "].  " +
-                        "Utilizing corresponding filter chain...");
-                }
-                return getChain(path, originalChain);
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the <code>FilterChain</code> to use for the specified application path, or <code>null</code> if the
-     * original <code>FilterChain</code> should be used.
-     * <p/>
-     * The default implementation simply calls <code>this.chains.get(chainUrl)</code> to acquire the configured
-     * <code>List&lt;Filter&gt;</code> filter chain.  If that configured chain is non-null and not empty, it is
-     * returned, otherwise <code>null</code> is returned to indicate that the <code>originalChain</code> should be
-     * used instead.
-     *
-     * @param chainUrl      the configured filter chain url
-     * @param originalChain the original FilterChain given by the Servlet container.
-     * @return the <code>FilterChain</code> to use for the specified application path, or <code>null</code> if the
-     *         original <code>FilterChain</code> should be used.
-     */
-    protected FilterChain getChain(String chainUrl, FilterChain originalChain) {
-        List<Filter> pathFilters = this.chains.get(chainUrl);
-        if (pathFilters != null && !pathFilters.isEmpty()) {
-            return createChain(pathFilters, originalChain);
-        }
-        return null;
-    }
-
-    /**
-     * Creates a new FilterChain based on the specified configured url filter chain and original chain.
-     * <p/>
-     * The input arguments are expected be be non-null and non-empty, since these conditions are accounted for in the
-     * {@link #getChain(String, javax.servlet.FilterChain) getChain(chainUrl,originalChain)} implementation that
-     * calls this method.
-     * <p/>
-     * The default implementation merely returns
-     * <code>new {@link org.apache.shiro.web.servlet.ProxiedFilterChain FilterChainWrapper(filters, originalChain)}</code>,
-     * and can be overridden by subclasses for custom creation.
-     *
-     * @param filters       the configured filter chain for the incoming request application path
-     * @param originalChain the original FilterChain given by the Servlet container.
-     * @return a new FilterChain based on the specified configured url filter chain and original chain.
-     */
-    protected FilterChain createChain(List<Filter> filters, FilterChain originalChain) {
-        return new ProxiedFilterChain(originalChain, filters);
-    }
-
-    /**
-     * Returns <code>true</code> if an incoming request's path (the <code>path</code> argument)
-     * matches a configured filter chain path in the <code>[urls]</code> section (the <code>pattern</code> argument),
-     * <code>false</code> otherwise.
-     * <p/>
-     * Simply delegates to
-     * <b><code>{@link #getPathMatcher() getPathMatcher()}.{@link org.apache.shiro.util.PatternMatcher#matches(String, String) matches(pattern,path)}</code></b>,
-     * but can be overridden by subclasses for custom matching behavior.
-     *
-     * @param pattern the pattern to match against
-     * @param path    the value to match with the specified <code>pattern</code>
-     * @return <code>true</code> if the request <code>path</code> matches the specified filter chain url <code>pattern</code>,
-     *         <code>false</code> otherwise.
-     */
-    protected boolean pathMatches(String pattern, String path) {
-        PatternMatcher pathMatcher = getPathMatcher();
-        return pathMatcher.matches(pattern, path);
-    }
-
-    /**
-     * Merely returns
-     * <code>WebUtils.{@link org.apache.shiro.web.WebUtils#getPathWithinApplication(javax.servlet.http.HttpServletRequest) getPathWithinApplication(request)}</code>
-     * and can be overridden by subclasses for custom request-to-application-path resolution behavior.
-     *
-     * @param request the incoming <code>ServletRequest</code>
-     * @return the request's path within the appliation.
-     */
-    protected String getPathWithinApplication(ServletRequest request) {
-        return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
+        return resolver.getChain(request, response, originalChain);
     }
 
     /**
@@ -253,24 +116,24 @@
 
         //urls section:
         section = sections.get(URLS);
-        this.chains = createChains(section, filters);
+        createChains(section);
 
-        initFilters(this.chains);
+        initFilters(filters);
     }
 
-    protected void initFilters(Map<String, List<Filter>> chains) {
-        if (chains == null || chains.isEmpty()) {
+
+    protected void initFilters(Map<String, Filter> filters) {
+        if (CollectionUtils.isEmpty(filters)) {
             return;
         }
 
-        //add 'em to a set so we only initialize each one once:
-        Set<Filter> filters = new LinkedHashSet<Filter>();
-        for (List<Filter> pathFilters : chains.values()) {
-            filters.addAll(pathFilters);
-        }
+        //add 'em to a set so we only initialize each one once, just in case
+        //for some reason a filter is mapped more than once:
+        Collection<Filter> values = filters.values();
+        Set<Filter> filtersToInit = new LinkedHashSet<Filter>(values);
 
         //now initialize each one:
-        for (Filter filter : filters) {
+        for (Filter filter : filtersToInit) {
             initFilter(filter);
         }
     }
@@ -291,11 +154,11 @@
     @SuppressWarnings({"unchecked"})
     protected Map<String, Filter> getFilters(Map<String, String> section) {
 
-        Map<String, Filter> filters = createDefaultFilters();
+        Map<String, Filter> filters = resolver.getFilterChainManager().getFilters();
 
         if (section != null && !section.isEmpty()) {
             ReflectionBuilder builder = new ReflectionBuilder(filters);
-            Map built = builder.buildObjects(section);
+            Map<String, ?> built = builder.buildObjects(section);
             assertFilters(built);
             filters = (Map<String, Filter>) built;
         }
@@ -317,140 +180,28 @@
     protected void assertFilter(String name, Object o) throws ConfigurationException {
         if (!(o instanceof Filter)) {
             String msg = "[" + FILTERS + "] section specified a filter named '" + name + "', which does not " +
-                "implement the " + Filter.class.getName() + " interface.  Only Filter implementations may be " +
-                "defined.";
+                    "implement the " + Filter.class.getName() + " interface.  Only Filter implementations may be " +
+                    "defined.";
             throw new ConfigurationException(msg);
         }
     }
 
-    protected Map<String, Filter> createDefaultFilters() {
-        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
-
-        String name = "anon";
-        AdviceFilter filter = new AnonymousFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "user";
-        filter = new UserFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "authc";
-        filter = new FormAuthenticationFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "authcBasic";
-        filter = new BasicHttpAuthenticationFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "roles";
-        filter = new RolesAuthorizationFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "perms";
-        filter = new PermissionsAuthorizationFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "port";
-        filter = new PortFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        name = "ssl";
-        filter = new SslFilter();
-        filter.setName(name);
-        filters.put(name, filter);
-
-        return filters;
-    }
-
-    public Map<String, List<Filter>> createChains(Map<String, String> urls, Map<String, Filter> filters) {
+    protected void createChains(Map<String, String> urls) {
         if (urls == null || urls.isEmpty()) {
             if (log.isDebugEnabled()) {
                 log.debug("No urls to process.");
             }
-            return null;
-        }
-        if (filters == null || filters.isEmpty()) {
-            if (log.isDebugEnabled()) {
-                log.debug("No filters to process.");
-            }
-            return null;
+            return;
         }
 
         if (log.isTraceEnabled()) {
             log.trace("Before url processing.");
         }
 
-        Map<String, List<Filter>> pathChains = new LinkedHashMap<String, List<Filter>>(urls.size());
-
         for (Map.Entry<String, String> entry : urls.entrySet()) {
             String path = entry.getKey();
             String value = entry.getValue();
-
-            if (log.isDebugEnabled()) {
-                log.debug("Processing path [" + path + "] with value [" + value + "]");
-            }
-
-            List<Filter> pathFilters = new ArrayList<Filter>();
-
-            //parse the value by tokenizing it to get the resulting filter-specific config entries
-            //
-            //e.g. for a value of
-            //
-            //     "authc, roles[admin,user], perms[file:edit]"
-            //
-            // the resulting token array would equal
-            //
-            //     { "authc", "roles[admin,user]", "perms[file:edit]" }
-            //
-            String[] filterTokens = split(value, ',', '[', ']', true, true);
-
-            //each token is specific to each filter.
-            //strip the name and extract any filter-specific config between brackets [ ]
-            for (String token : filterTokens) {
-                String[] nameAndConfig = token.split("\\[", 2);
-                String name = nameAndConfig[0];
-                String config = null;
-
-                if (nameAndConfig.length == 2) {
-                    config = nameAndConfig[1];
-                    //if there was an open bracket, there was a close bracket, so strip it too:
-                    config = config.substring(0, config.length() - 1);
-                }
-
-                //now we have the filter name, path and (possibly null) path-specific config.  Let's apply them:
-                Filter filter = filters.get(name);
-                if (filter == null) {
-                    String msg = "Path [" + path + "] specified a filter named '" + name + "', but that " +
-                        "filter has not been specified in the [" + FILTERS + "] section.";
-                    throw new ConfigurationException(msg);
-                }
-                if (filter instanceof PathConfigProcessor) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("Applying path [" + path + "] to filter [" + name + "] " +
-                            "with config [" + config + "]");
-                    }
-                    ((PathConfigProcessor) filter).processPathConfig(path, config);
-                }
-
-                pathFilters.add(filter);
-            }
-
-            if (!pathFilters.isEmpty()) {
-                pathChains.put(path, pathFilters);
-            }
+            resolver.getFilterChainManager().createChain(path, value);
         }
-
-        if (pathChains.isEmpty()) {
-            return null;
-        }
-
-        return pathChains;
     }
 }

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=814833&r1=814832&r2=814833&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 Mon Sep 14 20:33:51 2009
@@ -19,7 +19,7 @@
 package org.apache.shiro.web.config;
 
 import org.apache.shiro.config.Configuration;
-import org.apache.shiro.web.filter.FilterChainResolver;
+import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 
 /**
  * A {@code WebConfiguration} configures Shiro components in a web-enabled application.

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathConfigProcessor.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathConfigProcessor.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathConfigProcessor.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathConfigProcessor.java Mon Sep 14 20:33:51 2009
@@ -21,14 +21,20 @@
 import javax.servlet.Filter;
 
 /**
- * A PathConfigProcessor processes configuration entries on a per path (per url) basis.
+ * A PathConfigProcessor processes configuration entries on a per path (url) basis.
  *
  * @author Les Hazlewood
  * @since 0.9
  */
 public interface PathConfigProcessor {
 
-    //TODO - complete JavaDoc
-
+    /**
+     * Processes the specified {@code config}, unique to the given {@code path}, and returns the Filter that should
+     * execute for that path/config combination.
+     *
+     * @param path   the path for which the {@code config} should be applied
+     * @param config the configuration for the {@code Filter} specific to the given {@code path}
+     * @return the {@code Filter} that should execute for the given path/config combination.
+     */
     Filter processPathConfig(String path, String config);
 }

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authc/FormAuthenticationFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authc/FormAuthenticationFilter.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authc/FormAuthenticationFilter.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authc/FormAuthenticationFilter.java Mon Sep 14 20:33:51 2009
@@ -18,24 +18,23 @@
  */
 package org.apache.shiro.web.filter.authc;
 
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.subject.Subject;
 import org.apache.shiro.web.WebUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
 
 
 /**
  * Requires the requesting user to be authenticated for the request to continue, and if they are not, forces the user
  * to login via by redirecting them to the {@link #setLoginUrl(String) loginUrl} you configure.
- *
+ * <p/>
  * <p>This filter constructs a {@link UsernamePasswordToken UsernamePasswordToken} with the values found in
  * {@link #setUsernameParam(String) username}, {@link #setPasswordParam(String) password},
  * and {@link #setRememberMeParam(String) rememberMe} request parameters.  It then calls
@@ -44,12 +43,12 @@
  * {@link #isLoginSubmission(javax.servlet.ServletRequest, javax.servlet.ServletResponse) isLoginSubmission(request,response)}
  * is <code>true</code>, which by default occurs when the request is for the {@link #setLoginUrl(String) loginUrl} and
  * is a POST request.
- *
+ * <p/>
  * <p>If the login attempt fails, the resulting <code>AuthenticationException</code> fully qualified class name will
  * be set as a request attribute under the {@link #setFailureKeyAttribute(String) failureKeyAttribute} key.  This
  * FQCN can be used as an i18n key or lookup mechanism to explain to the user why their login attempt failed
  * (e.g. no account, incorrect password, etc).
- *
+ * <p/>
  * <p>If you would prefer to handle the authentication validation and login in your own code, consider using the
  * {@link PassThruAuthenticationFilter} instead, which allows requests to the
  * {@link #loginUrl} to pass through to your application's code directly.
@@ -81,6 +80,19 @@
         setLoginUrl(DEFAULT_LOGIN_URL);
     }
 
+    @Override
+    public void setLoginUrl(String loginUrl) {
+        String previous = getLoginUrl();
+        if (previous != null) {
+            this.appliedPaths.remove(previous);
+        }
+        super.setLoginUrl(loginUrl);
+        if (log.isTraceEnabled()) {
+            log.trace("Adding login url to applied paths.");
+        }
+        this.appliedPaths.put(getLoginUrl(), null);
+    }
+
     public String getUsernameParam() {
         return usernameParam;
     }
@@ -135,14 +147,6 @@
         this.failureKeyAttribute = failureKeyAttribute;
     }
 
-    @Override
-    protected void onFilterConfigSet() throws Exception {
-        if (log.isTraceEnabled()) {
-            log.trace("Adding default login url to applied paths.");
-        }
-        this.appliedPaths.put(getLoginUrl(), null);
-    }
-
     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
         if (isLoginRequest(request, response)) {
             if (isLoginSubmission(request, response)) {
@@ -176,6 +180,7 @@
      * @param response the outgoing ServletResponse.
      * @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
         return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
     }

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/PortFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/PortFilter.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/PortFilter.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/PortFilter.java Mon Sep 14 20:33:51 2009
@@ -29,20 +29,44 @@
 
 /**
  * A Filter that requires the request to be on a specific port, and if not, redirects to the same URL on that port.
+ * <p/>
+ * Example config:
+ * <pre>
+ * [filters]
+ * port.port = 80
+ * <p/>
+ * [urls]
+ * /some/path/** = port
+ * # override for just this path:
+ * /another/path/** = port[8080]
+ * </pre>
  *
  * @author Les Hazlewood
  * @since 1.0
  */
 public class PortFilter extends AuthorizationFilter {
 
+    public static final int DEFAULT_HTTP_PORT = 80;
+    public static final String HTTP_SCHEME = "http";
+
+    private int port = DEFAULT_HTTP_PORT;
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
     protected int toPort(Object mappedValue) {
         String[] ports = (String[]) mappedValue;
         if (ports == null || ports.length == 0) {
-            throw new ConfigurationException("PortFilter defined, but no port was specified!");
+            return getPort();
         }
         if (ports.length > 1) {
             throw new ConfigurationException("PortFilter can only be configured with a single port.  You have " +
-                "configured " + ports.length + ": " + StringUtils.toString(ports));
+                    "configured " + ports.length + ": " + StringUtils.toString(ports));
         }
         return Integer.parseInt(ports[0]);
     }
@@ -53,6 +77,16 @@
         return requiredPort == requestPort;
     }
 
+    protected String getScheme(String requestScheme, int port) {
+        if (port == DEFAULT_HTTP_PORT) {
+            return HTTP_SCHEME;
+        } else if (port == SslFilter.DEFAULT_HTTPS_PORT) {
+            return SslFilter.HTTPS_SCHEME;
+        } else {
+            return requestScheme;
+        }
+    }
+
     /**
      * Redirects the request to the same exact incoming URL, but with the port listed in the filter's configuration.
      *
@@ -67,14 +101,9 @@
         //just redirect to the specified port:
         int port = toPort(mappedValue);
 
-        StringBuffer sb = new StringBuffer();
+        String scheme = getScheme(request.getScheme(), port);
 
-        String scheme = request.getScheme();
-        if (port == 80) {
-            scheme = "http";
-        } else if (port == 443) {
-            scheme = "https";
-        }
+        StringBuffer sb = new StringBuffer();
         sb.append(scheme).append("://");
         sb.append(request.getServerName());
         if (port != 80 && port != 443) {
@@ -84,7 +113,7 @@
         if (request instanceof HttpServletRequest) {
             sb.append(WebUtils.toHttp(request).getRequestURI());
             String query = WebUtils.toHttp(request).getQueryString();
-            if ( query != null ) {
+            if (query != null) {
                 sb.append("?").append(query);
             }
         }

Modified: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/SslFilter.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/SslFilter.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/SslFilter.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/authz/SslFilter.java Mon Sep 14 20:33:51 2009
@@ -19,12 +19,15 @@
 package org.apache.shiro.web.filter.authz;
 
 /**
- * Convenience filter which requires a request to be over SSL.  This filter has the same effect as of using the
- * {@link PortFilter} with configuration defaulting to port {@code 443}.  That is, these two configs are the same:
- *
+ * Filter which requires a request to be over SSL.
+ * <p/> This filter has the same effect as of using the
+ * The {@link #getPort() port} property defaults to {@code 443} and also additionally guarantees that the
+ * request scheme is always 'https' (except for port 80, which retains the 'http' scheme).
+ * <p/>
+ * Example config:
  * <pre>
- * /some/path/** = port[443]
- * /some/path/** = ssl
+ * [urls]
+ * /secure/path/** = ssl
  * </pre>
  *
  * @author Les Hazlewood
@@ -32,14 +35,19 @@
  */
 public class SslFilter extends PortFilter {
 
-    public static final int DEFAULT_SSL_PORT = 443;
+    public static final int DEFAULT_HTTPS_PORT = 443;
+    public static final String HTTPS_SCHEME = "https";
+
+    public SslFilter() {
+        setPort(DEFAULT_HTTPS_PORT);
+    }
 
     @Override
-    protected int toPort(Object mappedValue) {
-        String[] ports = (String[]) mappedValue;
-        if (ports == null || ports.length == 0) {
-            return DEFAULT_SSL_PORT;
+    protected String getScheme(String requestScheme, int port) {
+        if (port == DEFAULT_HTTP_PORT) {
+            return PortFilter.HTTP_SCHEME;
+        } else {
+            return HTTPS_SCHEME;
         }
-        return super.toPort(mappedValue);
     }
 }

Copied: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java (from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/DefaultFilterChainManager.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java?p2=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java&p1=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/DefaultFilterChainManager.java&r1=814648&r2=814833&rev=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/DefaultFilterChainManager.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java Mon Sep 14 20:33:51 2009
@@ -16,14 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.web.filter;
+package org.apache.shiro.web.filter.mgt;
 
-import org.apache.shiro.ShiroException;
 import org.apache.shiro.config.ConfigurationException;
 import org.apache.shiro.util.CollectionUtils;
-import org.apache.shiro.util.Initializable;
-import org.apache.shiro.util.Nameable;
 import org.apache.shiro.util.StringUtils;
+import static org.apache.shiro.util.StringUtils.split;
+import org.apache.shiro.web.filter.PathConfigProcessor;
 import org.apache.shiro.web.filter.authc.AnonymousFilter;
 import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
@@ -45,7 +44,7 @@
 /**
  * @since 1.0
  */
-public class DefaultFilterChainManager implements FilterChainManager, Initializable {
+public class DefaultFilterChainManager implements FilterChainManager {
 
     private static transient final Logger log = LoggerFactory.getLogger(DefaultFilterChainManager.class);
 
@@ -58,11 +57,14 @@
     public DefaultFilterChainManager() {
         this.filters = new LinkedHashMap<String, Filter>();
         this.filterChains = new LinkedHashMap<String, NamedFilterList>();
+        addDefaultFilters(false);
     }
 
     public DefaultFilterChainManager(FilterConfig filterConfig) {
-        this();
-        init(filterConfig);
+        this.filters = new LinkedHashMap<String, Filter>();
+        this.filterChains = new LinkedHashMap<String, NamedFilterList>();
+        setFilterConfig(filterConfig);
+        addDefaultFilters(true);
     }
 
     /**
@@ -87,6 +89,7 @@
         return filters;
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public void setFilters(Map<String, Filter> filters) {
         this.filters = filters;
     }
@@ -95,22 +98,11 @@
         return filterChains;
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public void setFilterChains(Map<String, NamedFilterList> filterChains) {
         this.filterChains = filterChains;
     }
 
-    public void init() throws ShiroException {
-        if (this.filterConfig == null) {
-            throw new IllegalStateException("filterConfig attribute must be set.");
-        }
-        addDefaultFilters();
-    }
-
-    public void init(FilterConfig filterConfig) throws ShiroException {
-        setFilterConfig(filterConfig);
-        init();
-    }
-
     public Filter getFilter(String name) {
         return this.filters.get(name);
     }
@@ -123,6 +115,48 @@
         addFilter(name, filter, init, true);
     }
 
+    public void createChain(String chainName, String chainDefinition) {
+        if (!StringUtils.hasText(chainName)) {
+            throw new NullPointerException("chainName cannot be null or empty.");
+        }
+        if (!StringUtils.hasText(chainDefinition)) {
+            throw new NullPointerException("chainDefinition cannot be null or empty.");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");
+        }
+
+        //parse the value by tokenizing it to get the resulting filter-specific config entries
+        //
+        //e.g. for a value of
+        //
+        //     "authc, roles[admin,user], perms[file:edit]"
+        //
+        // the resulting token array would equal
+        //
+        //     { "authc", "roles[admin,user]", "perms[file:edit]" }
+        //
+        String[] filterTokens = split(chainDefinition, ',', '[', ']', true, true);
+
+        //each token is specific to each filter.
+        //strip the name and extract any filter-specific config between brackets [ ]
+        for (String token : filterTokens) {
+            String[] nameAndConfig = token.split("\\[", 2);
+            String name = nameAndConfig[0];
+            String config = null;
+
+            if (nameAndConfig.length == 2) {
+                config = nameAndConfig[1];
+                //if there was an open bracket, there was a close bracket, so strip it too:
+                config = config.substring(0, config.length() - 1);
+            }
+
+            //now we have the filter name, path and (possibly null) path-specific config.  Let's apply them:
+            addToChain(chainName, name, config);
+        }
+    }
+
     protected void addFilter(String name, Filter filter, boolean init, boolean overwrite) {
         Filter existing = getFilter(name);
         if (existing == null || overwrite) {
@@ -137,7 +171,26 @@
         addToChain(chainName, filterName, null);
     }
 
+    public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
+        Filter filter = getFilter(filterName);
+        if (filter == null) {
+            throw new IllegalArgumentException("There is no filter with name '" + filterName +
+                    "' to apply to chain [" + chainName + "] in the pool of available Filters.  Ensure a " +
+                    "filter with that name/path has first been registered with the addFilter method(s).");
+        }
+        if (StringUtils.hasText(chainSpecificFilterConfig)) {
+            applyChainConfig(chainName, filter, chainSpecificFilterConfig);
+        }
+
+        NamedFilterList chain = ensureChain(chainName);
+        chain.add(filter);
+    }
+
     protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
+        if (log.isDebugEnabled()) {
+            log.debug("Attempting to apply path [" + chainName + "] to filter [" + filter + "] " +
+                    "with config [" + chainSpecificFilterConfig + "]");
+        }
         if (filter instanceof PathConfigProcessor) {
             ((PathConfigProcessor) filter).processPathConfig(chainName, chainSpecificFilterConfig);
         } else {
@@ -145,7 +198,7 @@
                     "Filter instance is not an 'instanceof' " +
                     PathConfigProcessor.class.getName() + ".  This is required if the filter is to accept " +
                     "chain-specific configuration.";
-            throw new IllegalArgumentException(msg);
+            throw new ConfigurationException(msg);
         }
     }
 
@@ -158,20 +211,6 @@
         return chain;
     }
 
-    public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
-        Filter filter = getFilter(filterName);
-        if (filter == null) {
-            throw new IllegalArgumentException("There is no filter with name '" + filterName +
-                    "' to apply to chain [" + chainName + "]");
-        }
-        if (StringUtils.hasText(chainSpecificFilterConfig)) {
-            applyChainConfig(chainName, filter, chainSpecificFilterConfig);
-        }
-
-        NamedFilterList chain = ensureChain(chainName);
-        chain.add(filter);
-    }
-
     public NamedFilterList getChain(String chainName) {
         return this.filterChains.get(chainName);
     }
@@ -239,7 +278,7 @@
     /**
      * Initializes the filter by calling <code>filter.init( {@link #getFilterConfig() getFilterConfig()} );</code>.
      *
-     * @param filter the filter to initialize with the <code>FilterConfig</code>.
+     * @param filter the filter to initialize with the {@code FilterConfig}.
      */
     protected void initFilter(Filter filter) {
         FilterConfig filterConfig = getFilterConfig();
@@ -254,28 +293,14 @@
         }
     }
 
-    protected void createFilters() {
-        addDefaultFilters();
-    }
-
-    protected void addFilterIfNecessary(String name, Filter filter) {
-        if (getFilter(name) == null) {
-            //has not been added yet, so add it now:
-            if (filter instanceof Nameable) {
-                ((Nameable) filter).setName(name);
-            }
-            addFilter(name, filter);
-        }
-    }
-
-    protected void addDefaultFilters() {
-        addFilter("anon", new AnonymousFilter(), true, false);
-        addFilter("user", new UserFilter(), true, false);
-        addFilter("authc", new FormAuthenticationFilter(), true, false);
-        addFilter("authcBasic", new BasicHttpAuthenticationFilter(), true, false);
-        addFilter("roles", new RolesAuthorizationFilter(), true, false);
-        addFilter("perms", new PermissionsAuthorizationFilter(), true, false);
-        addFilter("port", new PortFilter(), true, false);
-        addFilter("ssl", new SslFilter(), true, false);
+    protected void addDefaultFilters(boolean init) {
+        addFilter("anon", new AnonymousFilter(), init, false);
+        addFilter("user", new UserFilter(), init, false);
+        addFilter("authc", new FormAuthenticationFilter(), init, false);
+        addFilter("authcBasic", new BasicHttpAuthenticationFilter(), init, false);
+        addFilter("roles", new RolesAuthorizationFilter(), init, false);
+        addFilter("perms", new PermissionsAuthorizationFilter(), init, false);
+        addFilter("port", new PortFilter(), init, false);
+        addFilter("ssl", new SslFilter(), init, false);
     }
 }

Copied: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java (from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainManager.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java?p2=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java&p1=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainManager.java&r1=814648&r2=814833&rev=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainManager.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java Mon Sep 14 20:33:51 2009
@@ -16,18 +16,31 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.web.filter;
+package org.apache.shiro.web.filter.mgt;
+
+import org.apache.shiro.config.ConfigurationException;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
+import java.util.Map;
 import java.util.Set;
 
 /**
+ * A {@code FilterChainManager} manages the creation and modification of {@link Filter} chains from an available pool
+ * of {@link Filter} instances.
+ *
  * @since 1.0
  */
 public interface FilterChainManager {
 
     /**
+     * Returns the pool of available {@code Filter}s managed by this manager, keyed by {@code name}.
+     *
+     * @return the pool of available {@code Filter}s managed by this manager, keyed by {@code name}.
+     */
+    Map<String, Filter> getFilters();
+
+    /**
      * Returns the filter chain identified by the specified {@code chainName} or {@code null} if there is no chain with
      * that name.
      *
@@ -89,6 +102,69 @@
     void addFilter(String name, Filter filter, boolean init);
 
     /**
+     * Creates a filter chain for the given {@code chainName} with the specified {@code chainDefinition}
+     * String.
+     * <h3>Conventional Use</h3>
+     * Because the {@code FilterChainManager} interface does not impose any restrictions on filter chain names,
+     * (it expects only Strings), a convenient convention is to make the chain name an actual URL path expression
+     * (such as an {@link org.apache.shiro.util.AntPathMatcher Ant path expression}).  For example:
+     * <p/>
+     * <code>createChain(<b><em>path_expression</em></b>, <em>path_specific_filter_chain_definition</em>);</code>
+     * This convention can be used by a {@link FilterChainResolver} to inspect request URL paths
+     * against the chain name (path) and, if a match is found, return the corresponding chain for runtime filtering.
+     * <h3>Chain Definition Format</h3>
+     * 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
+     * <ol>
+     * <li>{@code filterN} is the name of a filter previously
+     * {@link #addFilter(String, javax.servlet.Filter) registered} with the manager, and</li>
+     * <li>{@code [optional_configN]} is an optional bracketed string that has meaning for that particular filter for
+     * <em>this particular chain</em></li>
+     * </ol>
+     * If the filter does not need specific config for that chain name/URL path,
+     * you may discard the brackets - that is, {@code filterN[]} just becomes {@code filterN}.
+     * <p/>
+     * And because this method does create a chain, remember that order matters!  The comma-delimited filter tokens in
+     * the {@code chainDefinition} specify the chain's execution order.
+     * <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
+     * config, it doesn't have any config brackets {@code []}.
+     * <p/>
+     * <pre>/remoting/** = authcBasic, roles[b2bClient], perms[&quot;remote:invoke:wan,lan&quot;]</pre>
+     * This example by contrast uses the 'roles' and 'perms' filters which <em>do</em> use bracket notation.  This
+     * definition says:
+     * <p/>
+     * Construct a filter chain named '{@code /remoting/**}' which
+     * <ol>
+     * <li>ensures the user is first authenticated ({@code authcBasic}) then</li>
+     * <li>ensures that user has the {@code b2bClient} role, and then finally</li>
+     * <li>ensures that they have the {@code remote:invoke:lan,wan} permission.</li>
+     * </ol>
+     * <p/>
+     * <b>Note</b>: because elements within brackets [ ] can be comma-delimited themselves, you must quote the
+     * internal bracket definition if commas are needed (the above example has 'lan,wan').  If we didn't do that, the
+     * parser would interpret the chain definition as four tokens:
+     * <ol>
+     * <li>authcBasic</li>
+     * <li>roles[b2bclient]</li>
+     * <li>perms[remote:invoke:lan</li>
+     * <li>wan]</li>
+     * </ol>
+     * which is obviously incorrect.  So remember to use quotes if your internal bracket definitions need to use commas.
+     *
+     * @param chainName       the name to associate with the chain, conventionally a URL path pattern.
+     * @param chainDefinition the string-formatted chain definition used to construct an actual
+     *                        {@link NamedFilterList} chain instance.
+     * @see FilterChainResolver
+     * @see org.apache.shiro.util.AntPathMatcher AntPathMatcher
+     */
+    void createChain(String chainName, String chainDefinition);
+
+    /**
      * Adds (appends) a filter to the filter chain identified by the given {@code chainName}.  If there is no chain
      * with the given name, a new one is created and the filter will be the first in the chain.
      *
@@ -113,6 +189,10 @@
      *                                  filter chain.
      * @throws IllegalArgumentException if there is not a {@link #addFilter(String, javax.servlet.Filter) registered}
      *                                  filter under the given {@code filterName}
+     * @throws ConfigurationException   if the filter is not capable of accepting {@code chainSpecificFilterConfig}
+     *                                  (usually such filters implement the
+     *                                  {@link org.apache.shiro.web.filter.PathConfigProcessor PathConfigProcessor}
+     *                                  interface).
      */
-    void addToChain(String chainName, String filterName, String chainSpecificFilterConfig);
+    void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException;
 }

Copied: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainResolver.java (from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainResolver.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainResolver.java?p2=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainResolver.java&p1=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainResolver.java&r1=814648&r2=814833&rev=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/FilterChainResolver.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainResolver.java Mon Sep 14 20:33:51 2009
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.web.filter;
+package org.apache.shiro.web.filter.mgt;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletRequest;
@@ -37,16 +37,16 @@
 public interface FilterChainResolver {
 
     /**
-     * Returns the filter chain that should be executed for the given request, or <code>null</code> if the
+     * Returns the filter chain that should be executed for the given request, or {@code null} if the
      * original chain should be used.
      * <p/>
-     * <p>This method allows a implementation to define arbitrary security {@link javax.servlet.Filter Filter}
+     * 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</code> intercepted by the ShiroFilter.
-     * @return the filter chain that should be executed for the given request, or <code>null</code> if the
+     * @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.
      */
     FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);

Copied: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/NamedFilterList.java (from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/NamedFilterList.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/NamedFilterList.java?p2=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/NamedFilterList.java&p1=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/NamedFilterList.java&r1=814648&r2=814833&rev=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/NamedFilterList.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/NamedFilterList.java Mon Sep 14 20:33:51 2009
@@ -16,18 +16,35 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.web.filter;
+package org.apache.shiro.web.filter.mgt;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import java.util.List;
 
 /**
+ * A {@code NamedFilterList} is a {@code List} of {@code Filter} instances that is uniquely identified by a
+ * {@link #getName() name}.  It has the ability to generate new {@link FilterChain} instances reflecting this list's
+ * filter order via the {@link #proxy proxy} method.
+ *
  * @since 1.0
  */
 public interface NamedFilterList extends List<Filter> {
 
+    /**
+     * Returns the configuration-unique name assigned to this {@code Filter} list.
+     *
+     * @return the configuration-unique name assigned to this {@code Filter} list.
+     */
     String getName();
 
+    /**
+     * Returns a new {@code FilterChain} instance that will first execute this list's {@code Filter}s (in list order)
+     * and end with the execution of the given {@code filterChain} instance.
+     *
+     * @param filterChain the {@code FilterChain} instance to execute after this list's {@code Filter}s have executed.
+     * @return a new {@code FilterChain} instance that will first execute this list's {@code Filter}s (in list order)
+     *         and end with the execution of the given {@code filterChain} instance.
+     */
     FilterChain proxy(FilterChain filterChain);
 }

Copied: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolver.java (from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathMatchingFilterChainResolver.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolver.java?p2=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolver.java&p1=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathMatchingFilterChainResolver.java&r1=814648&r2=814833&rev=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/PathMatchingFilterChainResolver.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolver.java Mon Sep 14 20:33:51 2009
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.web.filter;
+package org.apache.shiro.web.filter.mgt;
 
 import org.apache.shiro.util.AntPathMatcher;
 import org.apache.shiro.util.PatternMatcher;
@@ -30,6 +30,13 @@
 import javax.servlet.ServletResponse;
 
 /**
+ * A {@code FilterChainResolver} that resolves {@link FilterChain}s based on url path
+ * matching, as determined by a configurable {@link #setPathMatcher(org.apache.shiro.util.PatternMatcher) PathMatcher}.
+ * <p/>
+ * This implementation functions by consulting a {@link org.apache.shiro.web.filter.mgt.FilterChainManager} for all configured filter chains (keyed
+ * by configured path pattern).  If an incoming Request path matches one of the configured path patterns (via
+ * the {@code PathMatcher}, the corresponding configured {@code FilterChain} is returned.
+ *
  * @since 1.0
  */
 public class PathMatchingFilterChainResolver implements FilterChainResolver {
@@ -38,14 +45,15 @@
 
     private FilterChainManager filterChainManager;
 
-    protected PatternMatcher pathMatcher;
+    private PatternMatcher pathMatcher;
 
     public PathMatchingFilterChainResolver() {
         this.pathMatcher = new AntPathMatcher();
+        this.filterChainManager = new DefaultFilterChainManager();
     }
 
     public PathMatchingFilterChainResolver(FilterConfig filterConfig) {
-        this();
+        this.pathMatcher = new AntPathMatcher();
         this.filterChainManager = new DefaultFilterChainManager(filterConfig);
     }
 
@@ -77,6 +85,7 @@
         return filterChainManager;
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public void setFilterChainManager(FilterChainManager filterChainManager) {
         this.filterChainManager = filterChainManager;
     }
@@ -107,13 +116,14 @@
     }
 
     /**
-     * Returns {@code true} if an incoming request's path (the {@code path} argument)
-     * matches a configured filter chain path in the {@code [urls]} section (the {@code pattern} argument),
-     * {@code false} otherwise.
+     * Returns {@code true} if an incoming request path (the {@code path} argument)
+     * matches a configured filter chain path (the {@code pattern} argument), {@code false} otherwise.
      * <p/>
      * Simply delegates to
-     * <b><code>{@link #getPathMatcher() getPathMatcher()}.{@link org.apache.shiro.util.PatternMatcher#matches(String, String) matches(pattern,path)}</code></b>,
-     * but can be overridden by subclasses for custom matching behavior.
+     * <b><code>{@link #getPathMatcher() getPathMatcher()}.{@link org.apache.shiro.util.PatternMatcher#matches(String, String) matches(pattern,path)}</code></b>.
+     * Subclass implementors should think carefully before overriding this method, as typically a custom
+     * {@code PathMatcher} should be configured for custom path matching behavior instead.  Favor OO composition
+     * rather than inheritance to limit your exposure to Shiro implementation details which may change over time.
      *
      * @param pattern the pattern to match against
      * @param path    the value to match with the specified {@code pattern}

Copied: incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/SimpleNamedFilterList.java (from r814648, incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/SimpleNamedFilterList.java)
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/SimpleNamedFilterList.java?p2=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/SimpleNamedFilterList.java&p1=incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/SimpleNamedFilterList.java&r1=814648&r2=814833&rev=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/SimpleNamedFilterList.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/filter/mgt/SimpleNamedFilterList.java Mon Sep 14 20:33:51 2009
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.web.filter;
+package org.apache.shiro.web.filter.mgt;
 
 import org.apache.shiro.util.StringUtils;
 import org.apache.shiro.web.servlet.ProxiedFilterChain;
@@ -26,6 +26,10 @@
 import java.util.*;
 
 /**
+ * Simple {@code NamedFilterList} implementation that is supported by a backing {@link List} instance and a simple
+ * {@link #getName() name} property. All {@link List} method implementations are immediately delegated to the
+ * wrapped backing list.
+ *
  * @since 1.0
  */
 public class SimpleNamedFilterList implements NamedFilterList {
@@ -33,14 +37,36 @@
     private String name;
     private List<Filter> backingList;
 
+    /**
+     * Creates a new {@code SimpleNamedFilterList} instance with the specified {@code name}, defaulting to a new
+     * {@link ArrayList ArrayList} instance as the backing list.
+     *
+     * @param name the name to assign to this instance.
+     * @throws IllegalArgumentException if {@code name} is null or empty.
+     */
     public SimpleNamedFilterList(String name) {
-        this.backingList = new ArrayList<Filter>();
+        this(name, new ArrayList<Filter>());
+    }
+
+    /**
+     * Creates a new {@code SimpleNamedFilterList} instance with the specified {@code name} and {@code backingList}.
+     *
+     * @param name        the name to assign to this instance.
+     * @param backingList the list instance used to back all of this class's {@link List} method implementations.
+     * @throws IllegalArgumentException if {@code name} is null or empty.
+     * @throws NullPointerException     if the backing list is {@code null}.
+     */
+    public SimpleNamedFilterList(String name, List<Filter> backingList) {
+        if (backingList == null) {
+            throw new NullPointerException("backingList constructor argument cannot be null.");
+        }
+        this.backingList = backingList;
         setName(name);
     }
 
     protected void setName(String name) {
         if (!StringUtils.hasText(name)) {
-            throw new IllegalArgumentException("Cannot specify an empty name.");
+            throw new IllegalArgumentException("Cannot specify a null or empty name.");
         }
         this.name = name;
     }

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=814833&r1=814832&r2=814833&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 Mon Sep 14 20:33:51 2009
@@ -241,4 +241,34 @@
      */
     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/ProxiedFilterChain.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ProxiedFilterChain.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ProxiedFilterChain.java (original)
+++ incubator/shiro/trunk/web/src/main/java/org/apache/shiro/web/servlet/ProxiedFilterChain.java Mon Sep 14 20:33:51 2009
@@ -18,17 +18,13 @@
  */
 package org.apache.shiro.web.servlet;
 
-import java.io.IOException;
-import java.util.List;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.servlet.*;
+import java.io.IOException;
+import java.util.List;
+
 /**
  * @author Les Hazlewood
  * @since 0.9
@@ -44,6 +40,9 @@
     private int index = 0;
 
     public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
+        if (orig == null) {
+            throw new NullPointerException("original FilterChain cannot be null.");
+        }
         this.orig = orig;
         this.filters = filters;
         this.index = 0;

Modified: incubator/shiro/trunk/web/src/test/java/org/apache/shiro/web/servlet/ShiroFilterTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/web/src/test/java/org/apache/shiro/web/servlet/ShiroFilterTest.java?rev=814833&r1=814832&r2=814833&view=diff
==============================================================================
--- incubator/shiro/trunk/web/src/test/java/org/apache/shiro/web/servlet/ShiroFilterTest.java (original)
+++ incubator/shiro/trunk/web/src/test/java/org/apache/shiro/web/servlet/ShiroFilterTest.java Mon Sep 14 20:33:51 2009
@@ -40,7 +40,7 @@
         mockFilterConfig = createMock(FilterConfig.class);
         mockServletContext = createMock(ServletContext.class);
 
-        expect(mockFilterConfig.getServletContext()).andReturn(mockServletContext);
+        expect(mockFilterConfig.getServletContext()).andReturn(mockServletContext).anyTimes();
         expect(mockFilterConfig.getInitParameter(ShiroFilter.CONFIG_CLASS_NAME_INIT_PARAM_NAME)).andReturn(null).once();
         expect(mockFilterConfig.getInitParameter(ShiroFilter.CONFIG_INIT_PARAM_NAME)).andReturn(config).once();
         expect(mockFilterConfig.getInitParameter(ShiroFilter.CONFIG_URL_INIT_PARAM_NAME)).andReturn(null).once();



Mime
View raw message